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
toil/test/utils/utilsTest.py
CHANGED
|
@@ -11,37 +11,35 @@
|
|
|
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 builtins
|
|
15
|
+
from collections.abc import Callable, Generator
|
|
14
16
|
import logging
|
|
15
17
|
import os
|
|
18
|
+
from pathlib import Path
|
|
16
19
|
import re
|
|
17
|
-
import shutil
|
|
18
20
|
import subprocess
|
|
19
21
|
import sys
|
|
20
22
|
import time
|
|
21
23
|
import uuid
|
|
22
|
-
from
|
|
23
|
-
|
|
24
|
+
from typing import Optional, Any, cast
|
|
24
25
|
import pytest
|
|
25
26
|
|
|
26
|
-
pkg_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) # noqa
|
|
27
|
-
sys.path.insert(0, pkg_root) # noqa
|
|
28
|
-
|
|
29
27
|
import toil
|
|
30
28
|
from toil import resolveEntryPoint
|
|
31
29
|
from toil.common import Config, Toil
|
|
32
30
|
from toil.job import Job
|
|
33
31
|
from toil.lib.bioio import system
|
|
32
|
+
from toil.fileStores.abstractFileStore import AbstractFileStore
|
|
34
33
|
from toil.test import (
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
slow,
|
|
34
|
+
get_data,
|
|
35
|
+
pneeds_aws_ec2 as needs_aws_ec2,
|
|
36
|
+
pneeds_cwl as needs_cwl,
|
|
37
|
+
pneeds_docker as needs_docker,
|
|
38
|
+
pintegrative as integrative,
|
|
39
|
+
pneeds_rsync3 as needs_rsync3,
|
|
40
|
+
pslow as slow,
|
|
43
41
|
)
|
|
44
|
-
from toil.test.sort.
|
|
42
|
+
from toil.test.sort.sort import makeFileToSort
|
|
45
43
|
from toil.utils.toilStats import get_stats, process_data
|
|
46
44
|
from toil.utils.toilStatus import ToilStatus
|
|
47
45
|
from toil.version import python
|
|
@@ -49,95 +47,67 @@ from toil.version import python
|
|
|
49
47
|
logger = logging.getLogger(__name__)
|
|
50
48
|
|
|
51
49
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
50
|
+
@pytest.fixture(scope="function")
|
|
51
|
+
def unsortedFile(tmp_path: Path) -> Generator[Path]:
|
|
52
|
+
try:
|
|
53
|
+
tempFile = tmp_path / "lines"
|
|
54
|
+
makeFileToSort(str(tempFile), 1000, 10)
|
|
55
|
+
yield tempFile
|
|
56
|
+
finally:
|
|
57
|
+
pass # no cleanup needed
|
|
57
58
|
|
|
58
|
-
def setUp(self):
|
|
59
|
-
super().setUp()
|
|
60
|
-
self.tempDir = self._createTempDir()
|
|
61
|
-
self.tempFile = get_temp_file(rootDir=self.tempDir)
|
|
62
|
-
self.outputFile = get_temp_file(rootDir=self.tempDir)
|
|
63
|
-
self.outputFile = "someSortedStuff.txt"
|
|
64
|
-
self.toilDir = os.path.join(self.tempDir, "jobstore")
|
|
65
|
-
self.assertFalse(os.path.exists(self.toilDir))
|
|
66
|
-
self.lines = 1000
|
|
67
|
-
self.lineLen = 10
|
|
68
|
-
self.N = 1000
|
|
69
|
-
makeFileToSort(self.tempFile, self.lines, self.lineLen)
|
|
70
|
-
# First make our own sorted version
|
|
71
|
-
with open(self.tempFile) as fileHandle:
|
|
72
|
-
self.correctSort = fileHandle.readlines()
|
|
73
|
-
self.correctSort.sort()
|
|
74
|
-
|
|
75
|
-
self.sort_workflow_cmd = [
|
|
76
|
-
python,
|
|
77
|
-
"-m",
|
|
78
|
-
"toil.test.sort.sort",
|
|
79
|
-
f"file:{self.toilDir}",
|
|
80
|
-
f"--fileToSort={self.tempFile}",
|
|
81
|
-
f"--outputFile={self.outputFile}",
|
|
82
|
-
"--clean=never",
|
|
83
|
-
]
|
|
84
59
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
60
|
+
@pytest.fixture(scope="function")
|
|
61
|
+
def correctSort(unsortedFile: Path) -> list[str]:
|
|
62
|
+
with unsortedFile.open() as fileHandle:
|
|
63
|
+
lines = fileHandle.readlines()
|
|
64
|
+
lines.sort()
|
|
65
|
+
return lines
|
|
91
66
|
|
|
92
|
-
def tearDown(self):
|
|
93
|
-
if os.path.exists(self.tempDir):
|
|
94
|
-
shutil.rmtree(self.tempDir)
|
|
95
|
-
if os.path.exists(self.toilDir):
|
|
96
|
-
shutil.rmtree(self.toilDir)
|
|
97
67
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
if os.path.exists(f):
|
|
104
|
-
os.remove(f)
|
|
68
|
+
class TestUtils:
|
|
69
|
+
"""
|
|
70
|
+
Tests the utilities that toil ships with, e.g. stats and status, in conjunction with restart
|
|
71
|
+
functionality.
|
|
72
|
+
"""
|
|
105
73
|
|
|
106
|
-
|
|
74
|
+
N: int = 1000
|
|
107
75
|
|
|
108
76
|
@property
|
|
109
|
-
def toilMain(self):
|
|
77
|
+
def toilMain(self) -> str:
|
|
110
78
|
return resolveEntryPoint("toil")
|
|
111
79
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
return [self.toilMain, "clean", self.toilDir]
|
|
80
|
+
def cleanCommand(self, jobstore: Path) -> list[str]:
|
|
81
|
+
return [self.toilMain, "clean", str(jobstore)]
|
|
115
82
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
return [self.toilMain, "stats", self.toilDir, "--pretty"]
|
|
83
|
+
def statsCommand(self, jobstore: Path) -> list[str]:
|
|
84
|
+
return [self.toilMain, "stats", str(jobstore), "--pretty"]
|
|
119
85
|
|
|
120
|
-
def statusCommand(
|
|
121
|
-
|
|
86
|
+
def statusCommand(
|
|
87
|
+
self, jobstore: Path, failIfNotComplete: bool = False
|
|
88
|
+
) -> list[str]:
|
|
89
|
+
commandTokens = [self.toilMain, "status", str(jobstore)]
|
|
122
90
|
if failIfNotComplete:
|
|
123
91
|
commandTokens.append("--failIfNotComplete")
|
|
124
92
|
return commandTokens
|
|
125
93
|
|
|
126
|
-
def test_config_functionality(self):
|
|
94
|
+
def test_config_functionality(self, tmp_path: Path) -> None:
|
|
127
95
|
"""Ensure that creating and reading back the config file works"""
|
|
128
|
-
config_file =
|
|
129
|
-
config_command = [self.toilMain, "config", config_file]
|
|
96
|
+
config_file = tmp_path / "config.yaml"
|
|
97
|
+
config_command = [self.toilMain, "config", str(config_file)]
|
|
130
98
|
# make sure the command `toil config file_path` works
|
|
131
99
|
try:
|
|
132
100
|
subprocess.check_call(config_command)
|
|
133
101
|
except subprocess.CalledProcessError:
|
|
134
|
-
|
|
102
|
+
pytest.fail("The toil config utility failed!")
|
|
135
103
|
|
|
136
104
|
parser = Job.Runner.getDefaultArgumentParser()
|
|
137
105
|
# make sure that toil can read from the generated config file
|
|
138
106
|
try:
|
|
139
|
-
parser.parse_args(
|
|
140
|
-
|
|
107
|
+
parser.parse_args(
|
|
108
|
+
[str(tmp_path / "random_jobstore"), "--config", str(config_file)]
|
|
109
|
+
)
|
|
110
|
+
with config_file.open() as cm:
|
|
141
111
|
payload = cm.read()
|
|
142
112
|
expected = "workDir batchSystem symlinkImports defaultMemory retryCount"
|
|
143
113
|
assert all(
|
|
@@ -145,16 +115,19 @@ class UtilsTest(ToilTest):
|
|
|
145
115
|
for param in expected.split(" ")
|
|
146
116
|
), f"Generated config contains { expected }"
|
|
147
117
|
except SystemExit:
|
|
148
|
-
|
|
149
|
-
finally:
|
|
150
|
-
os.remove(config_file)
|
|
118
|
+
pytest.fail("Failed to parse the default generated config file!")
|
|
151
119
|
|
|
152
120
|
@needs_rsync3
|
|
153
121
|
@pytest.mark.timeout(1200)
|
|
154
122
|
@needs_aws_ec2
|
|
155
123
|
@integrative
|
|
124
|
+
@pytest.mark.rsync
|
|
125
|
+
@pytest.mark.online
|
|
126
|
+
@pytest.mark.aws_s3
|
|
127
|
+
@pytest.mark.integrative
|
|
156
128
|
@slow
|
|
157
|
-
|
|
129
|
+
@pytest.mark.slow
|
|
130
|
+
def testAWSProvisionerUtils(self) -> None:
|
|
158
131
|
"""
|
|
159
132
|
Runs a number of the cluster utilities in sequence.
|
|
160
133
|
|
|
@@ -170,8 +143,8 @@ class UtilsTest(ToilTest):
|
|
|
170
143
|
"""
|
|
171
144
|
# TODO: Run these for the other clouds.
|
|
172
145
|
clusterName = f"cluster-utils-test{uuid.uuid4()}"
|
|
173
|
-
keyName = os.getenv("TOIL_AWS_KEYNAME").strip()
|
|
174
|
-
expected_owner = os.getenv("TOIL_OWNER_TAG"
|
|
146
|
+
keyName = os.getenv("TOIL_AWS_KEYNAME", "id_rsa").strip()
|
|
147
|
+
expected_owner = os.getenv("TOIL_OWNER_TAG", keyName)
|
|
175
148
|
|
|
176
149
|
try:
|
|
177
150
|
from toil.provisioners.aws.awsProvisioner import AWSProvisioner
|
|
@@ -215,7 +188,7 @@ class UtilsTest(ToilTest):
|
|
|
215
188
|
"Owner": expected_owner,
|
|
216
189
|
}
|
|
217
190
|
for key in tags:
|
|
218
|
-
|
|
191
|
+
assert cast(dict[str, str], leader.tags).get(key) == tags[key]
|
|
219
192
|
finally:
|
|
220
193
|
system(
|
|
221
194
|
[
|
|
@@ -228,22 +201,26 @@ class UtilsTest(ToilTest):
|
|
|
228
201
|
)
|
|
229
202
|
|
|
230
203
|
@slow
|
|
231
|
-
def testUtilsSort(
|
|
204
|
+
def testUtilsSort(
|
|
205
|
+
self, tmp_path: Path, unsortedFile: Path, correctSort: list[str]
|
|
206
|
+
) -> None:
|
|
232
207
|
"""
|
|
233
208
|
Tests the status and stats commands of the toil command line utility using the
|
|
234
209
|
sort example with the --restart flag.
|
|
235
210
|
"""
|
|
211
|
+
jobstore = tmp_path / "jobstore"
|
|
212
|
+
outputFile = tmp_path / "someSortedStuff.txt"
|
|
236
213
|
# Get the sort command to run
|
|
237
214
|
toilCommand = [
|
|
238
215
|
sys.executable,
|
|
239
216
|
"-m",
|
|
240
217
|
toil.test.sort.sort.__name__,
|
|
241
|
-
|
|
218
|
+
str(jobstore),
|
|
242
219
|
"--logLevel=DEBUG",
|
|
243
220
|
"--fileToSort",
|
|
244
|
-
|
|
221
|
+
str(unsortedFile),
|
|
245
222
|
"--outputFile",
|
|
246
|
-
|
|
223
|
+
str(outputFile),
|
|
247
224
|
"--N",
|
|
248
225
|
str(self.N),
|
|
249
226
|
"--stats",
|
|
@@ -252,11 +229,10 @@ class UtilsTest(ToilTest):
|
|
|
252
229
|
"--badWorkerFailInterval=0.05",
|
|
253
230
|
]
|
|
254
231
|
# Try restarting it to check that a JobStoreException is thrown
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
)
|
|
232
|
+
with pytest.raises(subprocess.CalledProcessError):
|
|
233
|
+
system(toilCommand + ["--restart"])
|
|
258
234
|
# Check that trying to run it in restart mode does not create the jobStore
|
|
259
|
-
|
|
235
|
+
assert not jobstore.exists()
|
|
260
236
|
|
|
261
237
|
# Status command
|
|
262
238
|
# Run the script for the first time
|
|
@@ -266,17 +242,15 @@ class UtilsTest(ToilTest):
|
|
|
266
242
|
except (
|
|
267
243
|
subprocess.CalledProcessError
|
|
268
244
|
): # This happens when the script fails due to having unfinished jobs
|
|
269
|
-
system(self.statusCommand())
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
system,
|
|
273
|
-
self.statusCommand(failIfNotComplete=True),
|
|
274
|
-
)
|
|
245
|
+
system(self.statusCommand(jobstore))
|
|
246
|
+
with pytest.raises(subprocess.CalledProcessError):
|
|
247
|
+
system(self.statusCommand(jobstore, failIfNotComplete=True))
|
|
275
248
|
finished = False
|
|
276
|
-
|
|
249
|
+
assert jobstore.exists()
|
|
277
250
|
|
|
278
251
|
# Try running it without restart and check an exception is thrown
|
|
279
|
-
|
|
252
|
+
with pytest.raises(subprocess.CalledProcessError):
|
|
253
|
+
system(toilCommand)
|
|
280
254
|
|
|
281
255
|
# Now restart it until done
|
|
282
256
|
totalTrys = 1
|
|
@@ -287,49 +261,48 @@ class UtilsTest(ToilTest):
|
|
|
287
261
|
except (
|
|
288
262
|
subprocess.CalledProcessError
|
|
289
263
|
): # This happens when the script fails due to having unfinished jobs
|
|
290
|
-
system(self.statusCommand())
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
system,
|
|
294
|
-
self.statusCommand(failIfNotComplete=True),
|
|
295
|
-
)
|
|
264
|
+
system(self.statusCommand(jobstore))
|
|
265
|
+
with pytest.raises(subprocess.CalledProcessError):
|
|
266
|
+
system(self.statusCommand(jobstore, failIfNotComplete=True))
|
|
296
267
|
if totalTrys > 16:
|
|
297
|
-
|
|
268
|
+
pytest.fail("Exceeded a reasonable number of restarts")
|
|
298
269
|
totalTrys += 1
|
|
299
270
|
|
|
300
271
|
# Check the toil status command does not issue an exception
|
|
301
|
-
system(self.statusCommand())
|
|
272
|
+
system(self.statusCommand(jobstore))
|
|
302
273
|
|
|
303
274
|
# Check we can run 'toil stats'
|
|
304
|
-
system(self.statsCommand)
|
|
275
|
+
system(self.statsCommand(jobstore))
|
|
305
276
|
|
|
306
277
|
# Check the file is properly sorted
|
|
307
|
-
with open(
|
|
278
|
+
with outputFile.open() as fileHandle:
|
|
308
279
|
l2 = fileHandle.readlines()
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
# Delete output file before next step
|
|
312
|
-
os.remove(self.outputFile)
|
|
280
|
+
assert correctSort == l2
|
|
313
281
|
|
|
314
282
|
# Check we can run 'toil clean'
|
|
315
|
-
system(self.cleanCommand)
|
|
283
|
+
system(self.cleanCommand(jobstore))
|
|
316
284
|
|
|
317
285
|
@slow
|
|
318
|
-
|
|
286
|
+
@pytest.mark.slow
|
|
287
|
+
def testUtilsStatsSort(
|
|
288
|
+
self, tmp_path: Path, unsortedFile: Path, correctSort: list[str]
|
|
289
|
+
) -> None:
|
|
319
290
|
"""
|
|
320
291
|
Tests the stats commands on a complete run of the stats test.
|
|
321
292
|
"""
|
|
293
|
+
jobstore = tmp_path / "jobstore"
|
|
294
|
+
outputFile = tmp_path / "someSortedStuff.txt"
|
|
322
295
|
# Get the sort command to run
|
|
323
296
|
toilCommand = [
|
|
324
297
|
sys.executable,
|
|
325
298
|
"-m",
|
|
326
299
|
toil.test.sort.sort.__name__,
|
|
327
|
-
|
|
300
|
+
str(jobstore),
|
|
328
301
|
"--logLevel=DEBUG",
|
|
329
302
|
"--fileToSort",
|
|
330
|
-
|
|
303
|
+
str(unsortedFile),
|
|
331
304
|
"--outputFile",
|
|
332
|
-
|
|
305
|
+
str(outputFile),
|
|
333
306
|
"--N",
|
|
334
307
|
str(self.N),
|
|
335
308
|
"--stats",
|
|
@@ -340,31 +313,29 @@ class UtilsTest(ToilTest):
|
|
|
340
313
|
|
|
341
314
|
# Run the script for the first time
|
|
342
315
|
system(toilCommand)
|
|
343
|
-
|
|
316
|
+
assert jobstore.exists()
|
|
344
317
|
|
|
345
318
|
# Check we can run 'toil stats'
|
|
346
|
-
system(self.statsCommand)
|
|
319
|
+
system(self.statsCommand(jobstore))
|
|
347
320
|
|
|
348
321
|
# Check the file is properly sorted
|
|
349
|
-
with open(
|
|
322
|
+
with outputFile.open() as fileHandle:
|
|
350
323
|
l2 = fileHandle.readlines()
|
|
351
|
-
|
|
324
|
+
assert correctSort == l2
|
|
352
325
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
def testUnicodeSupport(self):
|
|
357
|
-
options = Job.Runner.getDefaultOptions(self._getTestJobStorePath())
|
|
326
|
+
def testUnicodeSupport(self, tmp_path: Path) -> None:
|
|
327
|
+
options = Job.Runner.getDefaultOptions(tmp_path / "jobstore")
|
|
358
328
|
options.clean = "always"
|
|
359
329
|
options.logLevel = "debug"
|
|
360
330
|
Job.Runner.startToil(Job.wrapFn(printUnicodeCharacter), options)
|
|
361
331
|
|
|
362
332
|
@slow
|
|
363
|
-
|
|
333
|
+
@pytest.mark.slow
|
|
334
|
+
def testMultipleJobsPerWorkerStats(self, tmp_path: Path) -> None:
|
|
364
335
|
"""
|
|
365
336
|
Tests case where multiple jobs are run on 1 worker to ensure that all jobs report back their data
|
|
366
337
|
"""
|
|
367
|
-
options = Job.Runner.getDefaultOptions(
|
|
338
|
+
options = Job.Runner.getDefaultOptions(tmp_path / "jobstore")
|
|
368
339
|
options.clean = "never"
|
|
369
340
|
options.stats = True
|
|
370
341
|
Job.Runner.startToil(RunTwoJobsPerWorker(), options)
|
|
@@ -373,147 +344,253 @@ class UtilsTest(ToilTest):
|
|
|
373
344
|
jobStore = Toil.resumeJobStore(config.jobStore)
|
|
374
345
|
stats = get_stats(jobStore)
|
|
375
346
|
collatedStats = process_data(jobStore.config, stats)
|
|
376
|
-
|
|
377
|
-
len(collatedStats.job_types) == 2
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
347
|
+
assert (
|
|
348
|
+
len(collatedStats.job_types) == 2 # type: ignore[attr-defined]
|
|
349
|
+
), "Some jobs are not represented in the stats."
|
|
350
|
+
|
|
351
|
+
def check_status(
|
|
352
|
+
self,
|
|
353
|
+
jobstore: Path,
|
|
354
|
+
status: str,
|
|
355
|
+
status_fn: Callable[[str], str],
|
|
356
|
+
process: Optional[subprocess.Popen[Any]] = None,
|
|
357
|
+
seconds: int = 20,
|
|
358
|
+
) -> None:
|
|
382
359
|
time_elapsed = 0.0
|
|
383
360
|
has_stopped = process.poll() is not None if process else False
|
|
384
|
-
current_status = status_fn(
|
|
361
|
+
current_status = status_fn(str(jobstore))
|
|
385
362
|
while current_status != status:
|
|
386
363
|
if has_stopped:
|
|
387
364
|
# If the process has stopped and the stratus is wrong, it will never be right.
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
status
|
|
391
|
-
|
|
392
|
-
)
|
|
365
|
+
assert process is not None
|
|
366
|
+
assert (
|
|
367
|
+
current_status == status
|
|
368
|
+
), f"Process returned {process.returncode} without status reaching {status}; stuck at {current_status}"
|
|
393
369
|
logger.debug(
|
|
394
370
|
"Workflow is %s; waiting for %s (%s/%s elapsed)",
|
|
395
371
|
current_status,
|
|
396
372
|
status,
|
|
397
373
|
time_elapsed,
|
|
398
|
-
seconds
|
|
374
|
+
seconds,
|
|
399
375
|
)
|
|
400
376
|
time.sleep(0.5)
|
|
401
377
|
time_elapsed += 0.5
|
|
402
378
|
has_stopped = process.poll() is not None if process else False
|
|
403
|
-
current_status = status_fn(
|
|
379
|
+
current_status = status_fn(str(jobstore))
|
|
404
380
|
if time_elapsed > seconds:
|
|
405
|
-
|
|
406
|
-
current_status
|
|
407
|
-
|
|
408
|
-
f"Waited {seconds} seconds without status reaching {status}; stuck at {current_status}",
|
|
409
|
-
)
|
|
381
|
+
assert (
|
|
382
|
+
current_status == status
|
|
383
|
+
), "Waited {seconds} seconds without status reaching {status}; stuck at {current_status}"
|
|
410
384
|
|
|
411
|
-
def testGetPIDStatus(self):
|
|
385
|
+
def testGetPIDStatus(self, tmp_path: Path, unsortedFile: Path) -> None:
|
|
412
386
|
"""Test that ToilStatus.getPIDStatus() behaves as expected."""
|
|
413
|
-
|
|
414
|
-
|
|
387
|
+
jobstore = tmp_path / "jobstore"
|
|
388
|
+
outputFile = tmp_path / "someSortedStuff.txt"
|
|
389
|
+
wf = subprocess.Popen(
|
|
390
|
+
[
|
|
391
|
+
python,
|
|
392
|
+
"-m",
|
|
393
|
+
"toil.test.sort.sort",
|
|
394
|
+
jobstore.as_uri(),
|
|
395
|
+
f"--fileToSort={unsortedFile}",
|
|
396
|
+
f"--outputFile={outputFile}",
|
|
397
|
+
"--clean=never",
|
|
398
|
+
]
|
|
399
|
+
)
|
|
400
|
+
self.check_status(
|
|
401
|
+
jobstore,
|
|
402
|
+
"RUNNING",
|
|
403
|
+
status_fn=ToilStatus.getPIDStatus,
|
|
404
|
+
process=wf,
|
|
405
|
+
seconds=60,
|
|
406
|
+
)
|
|
415
407
|
wf.wait()
|
|
416
|
-
self.check_status(
|
|
408
|
+
self.check_status(
|
|
409
|
+
jobstore,
|
|
410
|
+
"COMPLETED",
|
|
411
|
+
status_fn=ToilStatus.getPIDStatus,
|
|
412
|
+
process=wf,
|
|
413
|
+
seconds=60,
|
|
414
|
+
)
|
|
417
415
|
|
|
418
416
|
# TODO: we need to reach into the FileJobStore's files and delete this
|
|
419
417
|
# shared file. We assume we know its internal layout.
|
|
420
|
-
os.remove(
|
|
421
|
-
self.check_status(
|
|
418
|
+
os.remove(jobstore / "files/shared/pid.log")
|
|
419
|
+
self.check_status(
|
|
420
|
+
jobstore,
|
|
421
|
+
"QUEUED",
|
|
422
|
+
status_fn=ToilStatus.getPIDStatus,
|
|
423
|
+
process=wf,
|
|
424
|
+
seconds=60,
|
|
425
|
+
)
|
|
422
426
|
|
|
423
|
-
def testGetStatusFailedToilWF(self):
|
|
427
|
+
def testGetStatusFailedToilWF(self, tmp_path: Path, unsortedFile: Path) -> None:
|
|
424
428
|
"""
|
|
425
429
|
Test that ToilStatus.getStatus() behaves as expected with a failing Toil workflow.
|
|
426
430
|
While this workflow could be called by importing and evoking its main function, doing so would remove the
|
|
427
431
|
opportunity to test the 'RUNNING' functionality of getStatus().
|
|
428
432
|
"""
|
|
429
433
|
# --badWorker is set to force failure.
|
|
430
|
-
|
|
431
|
-
|
|
434
|
+
jobstore = tmp_path / "jobstore"
|
|
435
|
+
outputFile = tmp_path / "someSortedStuff.txt"
|
|
436
|
+
wf = subprocess.Popen(
|
|
437
|
+
[
|
|
438
|
+
python,
|
|
439
|
+
"-m",
|
|
440
|
+
"toil.test.sort.sort",
|
|
441
|
+
jobstore.as_uri(),
|
|
442
|
+
f"--fileToSort={unsortedFile}",
|
|
443
|
+
f"--outputFile={outputFile}",
|
|
444
|
+
"--clean=never",
|
|
445
|
+
"--badWorker=1",
|
|
446
|
+
]
|
|
447
|
+
)
|
|
448
|
+
self.check_status(
|
|
449
|
+
jobstore, "RUNNING", status_fn=ToilStatus.getStatus, process=wf, seconds=60
|
|
450
|
+
)
|
|
432
451
|
wf.wait()
|
|
433
|
-
self.check_status(
|
|
452
|
+
self.check_status(
|
|
453
|
+
jobstore, "ERROR", status_fn=ToilStatus.getStatus, process=wf, seconds=60
|
|
454
|
+
)
|
|
434
455
|
|
|
435
456
|
@needs_cwl
|
|
436
457
|
@needs_docker
|
|
437
|
-
|
|
458
|
+
@pytest.mark.cwl
|
|
459
|
+
@pytest.mark.docker
|
|
460
|
+
@pytest.mark.online
|
|
461
|
+
def testGetStatusFailedCWLWF(self, tmp_path: Path) -> None:
|
|
438
462
|
"""Test that ToilStatus.getStatus() behaves as expected with a failing CWL workflow."""
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
"
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
463
|
+
jobstore = tmp_path / "jobstore"
|
|
464
|
+
with get_data("test/cwl/sorttool.cwl") as cwl_file:
|
|
465
|
+
with get_data("test/cwl/whale.txt") as input_file:
|
|
466
|
+
# --badWorker is set to force failure.
|
|
467
|
+
cmd = [
|
|
468
|
+
"toil-cwl-runner",
|
|
469
|
+
"--logDebug",
|
|
470
|
+
"--jobStore",
|
|
471
|
+
str(jobstore),
|
|
472
|
+
"--clean=never",
|
|
473
|
+
"--badWorker=1",
|
|
474
|
+
str(cwl_file),
|
|
475
|
+
"--reverse",
|
|
476
|
+
"--input",
|
|
477
|
+
str(input_file),
|
|
478
|
+
f"--outdir={tmp_path}",
|
|
479
|
+
]
|
|
480
|
+
logger.info("Run command: %s", " ".join(cmd))
|
|
481
|
+
wf = subprocess.Popen(cmd)
|
|
482
|
+
self.check_status(
|
|
483
|
+
jobstore,
|
|
484
|
+
"RUNNING",
|
|
485
|
+
status_fn=ToilStatus.getStatus,
|
|
486
|
+
process=wf,
|
|
487
|
+
seconds=60,
|
|
488
|
+
)
|
|
489
|
+
wf.wait()
|
|
490
|
+
self.check_status(
|
|
491
|
+
jobstore,
|
|
492
|
+
"ERROR",
|
|
493
|
+
status_fn=ToilStatus.getStatus,
|
|
494
|
+
process=wf,
|
|
495
|
+
seconds=60,
|
|
496
|
+
)
|
|
458
497
|
|
|
459
498
|
@needs_cwl
|
|
460
499
|
@needs_docker
|
|
461
|
-
|
|
500
|
+
@pytest.mark.cwl
|
|
501
|
+
@pytest.mark.docker
|
|
502
|
+
@pytest.mark.online
|
|
503
|
+
def testGetStatusSuccessfulCWLWF(self, tmp_path: Path) -> None:
|
|
462
504
|
"""Test that ToilStatus.getStatus() behaves as expected with a successful CWL workflow."""
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
"
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
505
|
+
jobstore = tmp_path / "jobstore"
|
|
506
|
+
with get_data("test/cwl/sorttool.cwl") as cwl_file:
|
|
507
|
+
with get_data("test/cwl/whale.txt") as input_file:
|
|
508
|
+
cmd = [
|
|
509
|
+
"toil-cwl-runner",
|
|
510
|
+
"--jobStore",
|
|
511
|
+
str(jobstore),
|
|
512
|
+
"--clean=never",
|
|
513
|
+
str(cwl_file),
|
|
514
|
+
"--reverse",
|
|
515
|
+
"--input",
|
|
516
|
+
str(input_file),
|
|
517
|
+
f"--outdir={tmp_path}",
|
|
518
|
+
]
|
|
519
|
+
wf = subprocess.Popen(cmd)
|
|
520
|
+
self.check_status(
|
|
521
|
+
jobstore,
|
|
522
|
+
"RUNNING",
|
|
523
|
+
status_fn=ToilStatus.getStatus,
|
|
524
|
+
process=wf,
|
|
525
|
+
seconds=60,
|
|
526
|
+
)
|
|
527
|
+
wf.wait()
|
|
528
|
+
self.check_status(
|
|
529
|
+
jobstore,
|
|
530
|
+
"COMPLETED",
|
|
531
|
+
status_fn=ToilStatus.getStatus,
|
|
532
|
+
process=wf,
|
|
533
|
+
seconds=60,
|
|
534
|
+
)
|
|
478
535
|
|
|
479
536
|
@needs_cwl
|
|
480
|
-
@
|
|
481
|
-
def testPrintJobLog(self,
|
|
537
|
+
@pytest.mark.cwl
|
|
538
|
+
def testPrintJobLog(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
482
539
|
"""Test that ToilStatus.printJobLog() reads the log from a failed command without error."""
|
|
540
|
+
jobstore = tmp_path / "jobstore"
|
|
541
|
+
print_args: list[str] = []
|
|
542
|
+
|
|
543
|
+
def fake_print(*args: Any, **kwargs: Any) -> None:
|
|
544
|
+
print_args.extend(args)
|
|
483
545
|
# Run a workflow that will always fail
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
546
|
+
with get_data("test/cwl/alwaysfails.cwl") as cwl_file:
|
|
547
|
+
cmd = [
|
|
548
|
+
"toil-cwl-runner",
|
|
549
|
+
"--logDebug",
|
|
550
|
+
"--jobStore",
|
|
551
|
+
str(jobstore),
|
|
552
|
+
"--clean=never",
|
|
553
|
+
f"--outdir={tmp_path/'outdir'}",
|
|
554
|
+
str(cwl_file),
|
|
555
|
+
"--message",
|
|
556
|
+
"Testing",
|
|
557
|
+
]
|
|
558
|
+
logger.info("Run command: %s", " ".join(cmd))
|
|
559
|
+
wf = subprocess.Popen(cmd)
|
|
560
|
+
wf.wait()
|
|
561
|
+
# print log and check output
|
|
562
|
+
status = ToilStatus(str(jobstore))
|
|
563
|
+
with monkeypatch.context() as m:
|
|
564
|
+
m.setattr(builtins, "print", fake_print)
|
|
565
|
+
status.printJobLog()
|
|
500
566
|
|
|
501
567
|
# Make sure it printed some kind of complaint about the missing command.
|
|
502
|
-
|
|
503
|
-
self.assertIn("invalidcommand", args[0])
|
|
568
|
+
assert "invalidcommand" in print_args[0]
|
|
504
569
|
|
|
505
570
|
@pytest.mark.timeout(1200)
|
|
506
|
-
def testRestartAttribute(self):
|
|
571
|
+
def testRestartAttribute(self, tmp_path: Path) -> None:
|
|
507
572
|
"""
|
|
508
573
|
Test that the job store is only destroyed when we observe a successful workflow run.
|
|
509
574
|
The following simulates a failing workflow that attempts to resume without restart().
|
|
510
575
|
In this case, the job store should not be destroyed until restart() is called.
|
|
511
576
|
"""
|
|
577
|
+
jobstore = tmp_path / "jobstore"
|
|
512
578
|
# Run a workflow that will always fail
|
|
513
|
-
cmd =
|
|
579
|
+
cmd = [
|
|
580
|
+
python,
|
|
581
|
+
"-m",
|
|
582
|
+
"toil.test.sort.restart_sort",
|
|
583
|
+
jobstore.as_uri(),
|
|
584
|
+
"--badWorker=1",
|
|
585
|
+
"--logDebug",
|
|
586
|
+
]
|
|
514
587
|
subprocess.run(cmd)
|
|
515
588
|
|
|
516
|
-
restart_cmd =
|
|
589
|
+
restart_cmd = [
|
|
590
|
+
python,
|
|
591
|
+
"-m",
|
|
592
|
+
"toil.test.sort.restart_sort",
|
|
593
|
+
jobstore.as_uri(),
|
|
517
594
|
"--badWorker=0",
|
|
518
595
|
"--logDebug",
|
|
519
596
|
"--restart",
|
|
@@ -521,23 +598,23 @@ class UtilsTest(ToilTest):
|
|
|
521
598
|
subprocess.run(restart_cmd)
|
|
522
599
|
|
|
523
600
|
# Check the job store exists after restart attempt
|
|
524
|
-
|
|
601
|
+
assert jobstore.exists()
|
|
525
602
|
|
|
526
603
|
successful_cmd = [
|
|
527
604
|
python,
|
|
528
605
|
"-m",
|
|
529
606
|
"toil.test.sort.sort",
|
|
530
607
|
"--logDebug",
|
|
531
|
-
|
|
608
|
+
jobstore.as_uri(),
|
|
532
609
|
"--restart",
|
|
533
610
|
]
|
|
534
611
|
subprocess.run(successful_cmd)
|
|
535
612
|
|
|
536
613
|
# Check the job store is destroyed after calling restart()
|
|
537
|
-
|
|
614
|
+
assert not jobstore.exists()
|
|
538
615
|
|
|
539
616
|
|
|
540
|
-
def printUnicodeCharacter():
|
|
617
|
+
def printUnicodeCharacter() -> None:
|
|
541
618
|
# We want to get a unicode character to stdout but we can't print it directly because of
|
|
542
619
|
# Python encoding issues. To work around this we print in a separate Python process. See
|
|
543
620
|
# http://stackoverflow.com/questions/492483/setting-the-correct-encoding-when-piping-stdout-in-python
|
|
@@ -549,8 +626,5 @@ class RunTwoJobsPerWorker(Job):
|
|
|
549
626
|
Runs child job with same resources as self in an attempt to chain the jobs on the same worker
|
|
550
627
|
"""
|
|
551
628
|
|
|
552
|
-
def
|
|
553
|
-
Job.__init__(self)
|
|
554
|
-
|
|
555
|
-
def run(self, fileStore):
|
|
629
|
+
def run(self, fileStore: AbstractFileStore) -> None:
|
|
556
630
|
self.addChildFn(printUnicodeCharacter)
|