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.
Files changed (254) hide show
  1. toil/__init__.py +0 -35
  2. toil/batchSystems/abstractBatchSystem.py +1 -1
  3. toil/batchSystems/abstractGridEngineBatchSystem.py +1 -1
  4. toil/batchSystems/awsBatch.py +1 -1
  5. toil/batchSystems/cleanup_support.py +1 -1
  6. toil/batchSystems/kubernetes.py +53 -7
  7. toil/batchSystems/local_support.py +1 -1
  8. toil/batchSystems/mesos/batchSystem.py +13 -8
  9. toil/batchSystems/mesos/test/__init__.py +3 -2
  10. toil/batchSystems/singleMachine.py +1 -1
  11. toil/batchSystems/slurm.py +27 -26
  12. toil/bus.py +5 -3
  13. toil/common.py +39 -11
  14. toil/cwl/cwltoil.py +1 -1
  15. toil/job.py +64 -49
  16. toil/jobStores/abstractJobStore.py +24 -3
  17. toil/jobStores/fileJobStore.py +25 -1
  18. toil/jobStores/googleJobStore.py +104 -30
  19. toil/leader.py +9 -0
  20. toil/lib/accelerators.py +3 -1
  21. toil/lib/aws/utils.py.orig +504 -0
  22. toil/lib/bioio.py +1 -1
  23. toil/lib/docker.py +252 -91
  24. toil/lib/dockstore.py +11 -3
  25. toil/lib/exceptions.py +5 -3
  26. toil/lib/history.py +87 -13
  27. toil/lib/history_submission.py +23 -9
  28. toil/lib/io.py +34 -22
  29. toil/lib/misc.py +7 -1
  30. toil/lib/resources.py +2 -1
  31. toil/lib/threading.py +11 -10
  32. toil/options/common.py +8 -0
  33. toil/options/wdl.py +11 -0
  34. toil/server/api_spec/LICENSE +201 -0
  35. toil/server/api_spec/README.rst +5 -0
  36. toil/server/cli/wes_cwl_runner.py +2 -1
  37. toil/test/__init__.py +275 -115
  38. toil/test/batchSystems/batchSystemTest.py +227 -205
  39. toil/test/batchSystems/test_slurm.py +27 -0
  40. toil/test/cactus/pestis.tar.gz +0 -0
  41. toil/test/conftest.py +7 -0
  42. toil/test/cwl/2.fasta +11 -0
  43. toil/test/cwl/2.fastq +12 -0
  44. toil/test/cwl/conftest.py +1 -1
  45. toil/test/cwl/cwlTest.py +999 -867
  46. toil/test/cwl/directory/directory/file.txt +15 -0
  47. toil/test/cwl/download_directory_file.json +4 -0
  48. toil/test/cwl/download_directory_s3.json +4 -0
  49. toil/test/cwl/download_file.json +6 -0
  50. toil/test/cwl/download_http.json +6 -0
  51. toil/test/cwl/download_https.json +6 -0
  52. toil/test/cwl/download_s3.json +6 -0
  53. toil/test/cwl/download_subdirectory_file.json +5 -0
  54. toil/test/cwl/download_subdirectory_s3.json +5 -0
  55. toil/test/cwl/empty.json +1 -0
  56. toil/test/cwl/mock_mpi/fake_mpi.yml +8 -0
  57. toil/test/cwl/mock_mpi/fake_mpi_run.py +42 -0
  58. toil/test/cwl/optional-file-exists.json +6 -0
  59. toil/test/cwl/optional-file-missing.json +6 -0
  60. toil/test/cwl/preemptible_expression.json +1 -0
  61. toil/test/cwl/revsort-job-missing.json +6 -0
  62. toil/test/cwl/revsort-job.json +6 -0
  63. toil/test/cwl/s3_secondary_file.json +16 -0
  64. toil/test/cwl/seqtk_seq_job.json +6 -0
  65. toil/test/cwl/stream.json +6 -0
  66. toil/test/cwl/test_filename_conflict_resolution.ms/table.dat +0 -0
  67. toil/test/cwl/test_filename_conflict_resolution.ms/table.f0 +0 -0
  68. toil/test/cwl/test_filename_conflict_resolution.ms/table.f1 +0 -0
  69. toil/test/cwl/test_filename_conflict_resolution.ms/table.f1i +0 -0
  70. toil/test/cwl/test_filename_conflict_resolution.ms/table.f2 +0 -0
  71. toil/test/cwl/test_filename_conflict_resolution.ms/table.f2_TSM0 +0 -0
  72. toil/test/cwl/test_filename_conflict_resolution.ms/table.f3 +0 -0
  73. toil/test/cwl/test_filename_conflict_resolution.ms/table.f3_TSM0 +0 -0
  74. toil/test/cwl/test_filename_conflict_resolution.ms/table.f4 +0 -0
  75. toil/test/cwl/test_filename_conflict_resolution.ms/table.f4_TSM0 +0 -0
  76. toil/test/cwl/test_filename_conflict_resolution.ms/table.f5 +0 -0
  77. toil/test/cwl/test_filename_conflict_resolution.ms/table.info +0 -0
  78. toil/test/cwl/test_filename_conflict_resolution.ms/table.lock +0 -0
  79. toil/test/cwl/whale.txt +16 -0
  80. toil/test/docs/scripts/example_alwaysfail.py +38 -0
  81. toil/test/docs/scripts/example_alwaysfail_with_files.wdl +33 -0
  82. toil/test/docs/scripts/example_cachingbenchmark.py +117 -0
  83. toil/test/docs/scripts/stagingExampleFiles/in.txt +1 -0
  84. toil/test/docs/scripts/stagingExampleFiles/out.txt +2 -0
  85. toil/test/docs/scripts/tutorial_arguments.py +23 -0
  86. toil/test/docs/scripts/tutorial_debugging.patch +12 -0
  87. toil/test/docs/scripts/tutorial_debugging_hangs.wdl +126 -0
  88. toil/test/docs/scripts/tutorial_debugging_works.wdl +129 -0
  89. toil/test/docs/scripts/tutorial_docker.py +20 -0
  90. toil/test/docs/scripts/tutorial_dynamic.py +24 -0
  91. toil/test/docs/scripts/tutorial_encapsulation.py +28 -0
  92. toil/test/docs/scripts/tutorial_encapsulation2.py +29 -0
  93. toil/test/docs/scripts/tutorial_helloworld.py +15 -0
  94. toil/test/docs/scripts/tutorial_invokeworkflow.py +27 -0
  95. toil/test/docs/scripts/tutorial_invokeworkflow2.py +30 -0
  96. toil/test/docs/scripts/tutorial_jobfunctions.py +22 -0
  97. toil/test/docs/scripts/tutorial_managing.py +29 -0
  98. toil/test/docs/scripts/tutorial_managing2.py +56 -0
  99. toil/test/docs/scripts/tutorial_multiplejobs.py +25 -0
  100. toil/test/docs/scripts/tutorial_multiplejobs2.py +21 -0
  101. toil/test/docs/scripts/tutorial_multiplejobs3.py +22 -0
  102. toil/test/docs/scripts/tutorial_promises.py +25 -0
  103. toil/test/docs/scripts/tutorial_promises2.py +30 -0
  104. toil/test/docs/scripts/tutorial_quickstart.py +22 -0
  105. toil/test/docs/scripts/tutorial_requirements.py +44 -0
  106. toil/test/docs/scripts/tutorial_services.py +45 -0
  107. toil/test/docs/scripts/tutorial_staging.py +45 -0
  108. toil/test/docs/scripts/tutorial_stats.py +64 -0
  109. toil/test/lib/aws/test_iam.py +3 -1
  110. toil/test/lib/dockerTest.py +205 -122
  111. toil/test/lib/test_history.py +101 -77
  112. toil/test/provisioners/aws/awsProvisionerTest.py +12 -9
  113. toil/test/provisioners/clusterTest.py +4 -4
  114. toil/test/provisioners/gceProvisionerTest.py +16 -14
  115. toil/test/sort/sort.py +4 -1
  116. toil/test/src/busTest.py +17 -17
  117. toil/test/src/deferredFunctionTest.py +145 -132
  118. toil/test/src/importExportFileTest.py +71 -63
  119. toil/test/src/jobEncapsulationTest.py +27 -28
  120. toil/test/src/jobServiceTest.py +149 -133
  121. toil/test/src/jobTest.py +219 -211
  122. toil/test/src/miscTests.py +66 -60
  123. toil/test/src/promisedRequirementTest.py +163 -169
  124. toil/test/src/regularLogTest.py +24 -24
  125. toil/test/src/resourceTest.py +82 -76
  126. toil/test/src/restartDAGTest.py +51 -47
  127. toil/test/src/resumabilityTest.py +24 -19
  128. toil/test/src/retainTempDirTest.py +60 -57
  129. toil/test/src/systemTest.py +17 -13
  130. toil/test/src/threadingTest.py +29 -32
  131. toil/test/utils/ABCWorkflowDebug/B_file.txt +1 -0
  132. toil/test/utils/ABCWorkflowDebug/debugWorkflow.py +204 -0
  133. toil/test/utils/ABCWorkflowDebug/mkFile.py +16 -0
  134. toil/test/utils/ABCWorkflowDebug/sleep.cwl +12 -0
  135. toil/test/utils/ABCWorkflowDebug/sleep.yaml +1 -0
  136. toil/test/utils/toilDebugTest.py +117 -102
  137. toil/test/utils/toilKillTest.py +54 -53
  138. toil/test/utils/utilsTest.py +303 -229
  139. toil/test/wdl/lint_error.wdl +9 -0
  140. toil/test/wdl/md5sum/empty_file.json +1 -0
  141. toil/test/wdl/md5sum/md5sum-gs.json +1 -0
  142. toil/test/wdl/md5sum/md5sum.1.0.wdl +32 -0
  143. toil/test/wdl/md5sum/md5sum.input +1 -0
  144. toil/test/wdl/md5sum/md5sum.json +1 -0
  145. toil/test/wdl/md5sum/md5sum.wdl +25 -0
  146. toil/test/wdl/miniwdl_self_test/inputs-namespaced.json +1 -0
  147. toil/test/wdl/miniwdl_self_test/inputs.json +1 -0
  148. toil/test/wdl/miniwdl_self_test/self_test.wdl +40 -0
  149. toil/test/wdl/standard_library/as_map.json +16 -0
  150. toil/test/wdl/standard_library/as_map_as_input.wdl +23 -0
  151. toil/test/wdl/standard_library/as_pairs.json +7 -0
  152. toil/test/wdl/standard_library/as_pairs_as_input.wdl +23 -0
  153. toil/test/wdl/standard_library/ceil.json +3 -0
  154. toil/test/wdl/standard_library/ceil_as_command.wdl +16 -0
  155. toil/test/wdl/standard_library/ceil_as_input.wdl +16 -0
  156. toil/test/wdl/standard_library/collect_by_key.json +1 -0
  157. toil/test/wdl/standard_library/collect_by_key_as_input.wdl +23 -0
  158. toil/test/wdl/standard_library/cross.json +11 -0
  159. toil/test/wdl/standard_library/cross_as_input.wdl +19 -0
  160. toil/test/wdl/standard_library/flatten.json +7 -0
  161. toil/test/wdl/standard_library/flatten_as_input.wdl +18 -0
  162. toil/test/wdl/standard_library/floor.json +3 -0
  163. toil/test/wdl/standard_library/floor_as_command.wdl +16 -0
  164. toil/test/wdl/standard_library/floor_as_input.wdl +16 -0
  165. toil/test/wdl/standard_library/keys.json +8 -0
  166. toil/test/wdl/standard_library/keys_as_input.wdl +24 -0
  167. toil/test/wdl/standard_library/length.json +7 -0
  168. toil/test/wdl/standard_library/length_as_input.wdl +16 -0
  169. toil/test/wdl/standard_library/length_as_input_with_map.json +7 -0
  170. toil/test/wdl/standard_library/length_as_input_with_map.wdl +17 -0
  171. toil/test/wdl/standard_library/length_invalid.json +3 -0
  172. toil/test/wdl/standard_library/range.json +3 -0
  173. toil/test/wdl/standard_library/range_0.json +3 -0
  174. toil/test/wdl/standard_library/range_as_input.wdl +17 -0
  175. toil/test/wdl/standard_library/range_invalid.json +3 -0
  176. toil/test/wdl/standard_library/read_boolean.json +3 -0
  177. toil/test/wdl/standard_library/read_boolean_as_command.wdl +17 -0
  178. toil/test/wdl/standard_library/read_float.json +3 -0
  179. toil/test/wdl/standard_library/read_float_as_command.wdl +17 -0
  180. toil/test/wdl/standard_library/read_int.json +3 -0
  181. toil/test/wdl/standard_library/read_int_as_command.wdl +17 -0
  182. toil/test/wdl/standard_library/read_json.json +3 -0
  183. toil/test/wdl/standard_library/read_json_as_output.wdl +31 -0
  184. toil/test/wdl/standard_library/read_lines.json +3 -0
  185. toil/test/wdl/standard_library/read_lines_as_output.wdl +31 -0
  186. toil/test/wdl/standard_library/read_map.json +3 -0
  187. toil/test/wdl/standard_library/read_map_as_output.wdl +31 -0
  188. toil/test/wdl/standard_library/read_string.json +3 -0
  189. toil/test/wdl/standard_library/read_string_as_command.wdl +17 -0
  190. toil/test/wdl/standard_library/read_tsv.json +3 -0
  191. toil/test/wdl/standard_library/read_tsv_as_output.wdl +31 -0
  192. toil/test/wdl/standard_library/round.json +3 -0
  193. toil/test/wdl/standard_library/round_as_command.wdl +16 -0
  194. toil/test/wdl/standard_library/round_as_input.wdl +16 -0
  195. toil/test/wdl/standard_library/size.json +3 -0
  196. toil/test/wdl/standard_library/size_as_command.wdl +17 -0
  197. toil/test/wdl/standard_library/size_as_output.wdl +36 -0
  198. toil/test/wdl/standard_library/stderr.json +3 -0
  199. toil/test/wdl/standard_library/stderr_as_output.wdl +30 -0
  200. toil/test/wdl/standard_library/stdout.json +3 -0
  201. toil/test/wdl/standard_library/stdout_as_output.wdl +30 -0
  202. toil/test/wdl/standard_library/sub.json +3 -0
  203. toil/test/wdl/standard_library/sub_as_input.wdl +17 -0
  204. toil/test/wdl/standard_library/sub_as_input_with_file.wdl +17 -0
  205. toil/test/wdl/standard_library/transpose.json +6 -0
  206. toil/test/wdl/standard_library/transpose_as_input.wdl +18 -0
  207. toil/test/wdl/standard_library/write_json.json +6 -0
  208. toil/test/wdl/standard_library/write_json_as_command.wdl +17 -0
  209. toil/test/wdl/standard_library/write_lines.json +7 -0
  210. toil/test/wdl/standard_library/write_lines_as_command.wdl +17 -0
  211. toil/test/wdl/standard_library/write_map.json +6 -0
  212. toil/test/wdl/standard_library/write_map_as_command.wdl +17 -0
  213. toil/test/wdl/standard_library/write_tsv.json +6 -0
  214. toil/test/wdl/standard_library/write_tsv_as_command.wdl +17 -0
  215. toil/test/wdl/standard_library/zip.json +12 -0
  216. toil/test/wdl/standard_library/zip_as_input.wdl +19 -0
  217. toil/test/wdl/test.csv +3 -0
  218. toil/test/wdl/test.tsv +3 -0
  219. toil/test/wdl/testfiles/croo.wdl +38 -0
  220. toil/test/wdl/testfiles/drop_files.wdl +62 -0
  221. toil/test/wdl/testfiles/drop_files_subworkflow.wdl +13 -0
  222. toil/test/wdl/testfiles/empty.txt +0 -0
  223. toil/test/wdl/testfiles/not_enough_outputs.wdl +33 -0
  224. toil/test/wdl/testfiles/random.wdl +66 -0
  225. toil/test/wdl/testfiles/string_file_coercion.json +1 -0
  226. toil/test/wdl/testfiles/string_file_coercion.wdl +35 -0
  227. toil/test/wdl/testfiles/test.json +4 -0
  228. toil/test/wdl/testfiles/test_boolean.txt +1 -0
  229. toil/test/wdl/testfiles/test_float.txt +1 -0
  230. toil/test/wdl/testfiles/test_int.txt +1 -0
  231. toil/test/wdl/testfiles/test_lines.txt +5 -0
  232. toil/test/wdl/testfiles/test_map.txt +2 -0
  233. toil/test/wdl/testfiles/test_string.txt +1 -0
  234. toil/test/wdl/testfiles/url_to_file.wdl +13 -0
  235. toil/test/wdl/testfiles/url_to_optional_file.wdl +13 -0
  236. toil/test/wdl/testfiles/vocab.json +1 -0
  237. toil/test/wdl/testfiles/vocab.wdl +66 -0
  238. toil/test/wdl/testfiles/wait.wdl +34 -0
  239. toil/test/wdl/wdl_specification/type_pair.json +23 -0
  240. toil/test/wdl/wdl_specification/type_pair_basic.wdl +36 -0
  241. toil/test/wdl/wdl_specification/type_pair_with_files.wdl +36 -0
  242. toil/test/wdl/wdl_specification/v1_spec.json +1 -0
  243. toil/test/wdl/wdl_specification/v1_spec_declaration.wdl +39 -0
  244. toil/test/wdl/wdltoil_test.py +680 -407
  245. toil/test/wdl/wdltoil_test_kubernetes.py +2 -2
  246. toil/version.py +9 -9
  247. toil/wdl/wdltoil.py +336 -123
  248. {toil-8.1.0b1.dist-info → toil-8.2.0.dist-info}/METADATA +5 -4
  249. toil-8.2.0.dist-info/RECORD +439 -0
  250. {toil-8.1.0b1.dist-info → toil-8.2.0.dist-info}/WHEEL +1 -1
  251. toil-8.1.0b1.dist-info/RECORD +0 -259
  252. {toil-8.1.0b1.dist-info → toil-8.2.0.dist-info}/entry_points.txt +0 -0
  253. {toil-8.1.0b1.dist-info → toil-8.2.0.dist-info/licenses}/LICENSE +0 -0
  254. {toil-8.1.0b1.dist-info → toil-8.2.0.dist-info}/top_level.txt +0 -0
@@ -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 unittest.mock import patch
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
- ToilTest,
36
- get_temp_file,
37
- integrative,
38
- needs_aws_ec2,
39
- needs_cwl,
40
- needs_docker,
41
- needs_rsync3,
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.sortTest import makeFileToSort
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
- class UtilsTest(ToilTest):
53
- """
54
- Tests the utilities that toil ships with, e.g. stats and status, in conjunction with restart
55
- functionality.
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
- self.restart_sort_workflow_cmd = [
86
- python,
87
- "-m",
88
- "toil.test.sort.restart_sort",
89
- f"file:{self.toilDir}",
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
- for f in [
99
- self.tempFile,
100
- self.outputFile,
101
- os.path.join(self.tempDir, "output.txt"),
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
- ToilTest.tearDown(self)
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
- @property
113
- def cleanCommand(self):
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
- @property
117
- def statsCommand(self):
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(self, failIfNotComplete=False):
121
- commandTokens = [self.toilMain, "status", self.toilDir]
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 = os.path.abspath("config.yaml")
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
- self.fail("The toil config utility failed!")
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(["random_jobstore", "--config", config_file])
140
- with open(config_file) as cm:
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
- self.fail("Failed to parse the default generated config file!")
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
- def testAWSProvisionerUtils(self):
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() or "id_rsa"
174
- expected_owner = os.getenv("TOIL_OWNER_TAG") or keyName
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
- self.assertEqual(leader.tags.get(key), tags[key])
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(self):
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
- self.toilDir,
218
+ str(jobstore),
242
219
  "--logLevel=DEBUG",
243
220
  "--fileToSort",
244
- self.tempFile,
221
+ str(unsortedFile),
245
222
  "--outputFile",
246
- self.outputFile,
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
- self.assertRaises(
256
- subprocess.CalledProcessError, system, toilCommand + ["--restart"]
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
- self.assertFalse(os.path.exists(self.toilDir))
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
- self.assertRaises(
271
- subprocess.CalledProcessError,
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
- self.assertTrue(os.path.exists(self.toilDir))
249
+ assert jobstore.exists()
277
250
 
278
251
  # Try running it without restart and check an exception is thrown
279
- self.assertRaises(subprocess.CalledProcessError, system, toilCommand)
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
- self.assertRaises(
292
- subprocess.CalledProcessError,
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
- self.fail() # Exceeded a reasonable number of restarts
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(self.outputFile) as fileHandle:
278
+ with outputFile.open() as fileHandle:
308
279
  l2 = fileHandle.readlines()
309
- self.assertEqual(self.correctSort, l2)
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
- def testUtilsStatsSort(self):
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
- self.toilDir,
300
+ str(jobstore),
328
301
  "--logLevel=DEBUG",
329
302
  "--fileToSort",
330
- self.tempFile,
303
+ str(unsortedFile),
331
304
  "--outputFile",
332
- self.outputFile,
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
- self.assertTrue(os.path.exists(self.toilDir))
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(self.outputFile) as fileHandle:
322
+ with outputFile.open() as fileHandle:
350
323
  l2 = fileHandle.readlines()
351
- self.assertEqual(self.correctSort, l2)
324
+ assert correctSort == l2
352
325
 
353
- # Delete output file
354
- os.remove(self.outputFile)
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
- def testMultipleJobsPerWorkerStats(self):
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(self._getTestJobStorePath())
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
- self.assertTrue(
377
- len(collatedStats.job_types) == 2,
378
- "Some jobs are not represented in the stats.",
379
- )
380
-
381
- def check_status(self, status, status_fn, process=None, seconds=20):
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(self.toilDir)
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
- self.assertEqual(
389
- current_status,
390
- status,
391
- f"Process returned {process.returncode} without status reaching {status}; stuck at {current_status}",
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(self.toilDir)
379
+ current_status = status_fn(str(jobstore))
404
380
  if time_elapsed > seconds:
405
- self.assertEqual(
406
- current_status,
407
- status,
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
- wf = subprocess.Popen(self.sort_workflow_cmd)
414
- self.check_status("RUNNING", status_fn=ToilStatus.getPIDStatus, process=wf, seconds=60)
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("COMPLETED", status_fn=ToilStatus.getPIDStatus, process=wf, seconds=60)
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(os.path.join(self.toilDir, "files/shared/pid.log"))
421
- self.check_status("QUEUED", status_fn=ToilStatus.getPIDStatus, process=wf, seconds=60)
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
- wf = subprocess.Popen(self.sort_workflow_cmd + ["--badWorker=1"])
431
- self.check_status("RUNNING", status_fn=ToilStatus.getStatus, process=wf, seconds=60)
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("ERROR", status_fn=ToilStatus.getStatus, process=wf, seconds=60)
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
- def testGetStatusFailedCWLWF(self):
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
- # --badWorker is set to force failure.
440
- cmd = [
441
- "toil-cwl-runner",
442
- "--logDebug",
443
- "--jobStore",
444
- self.toilDir,
445
- "--clean=never",
446
- "--badWorker=1",
447
- "src/toil/test/cwl/sorttool.cwl",
448
- "--reverse",
449
- "--input",
450
- "src/toil/test/cwl/whale.txt",
451
- f"--outdir={self.tempDir}",
452
- ]
453
- logger.info("Run command: %s", " ".join(cmd))
454
- wf = subprocess.Popen(cmd)
455
- self.check_status("RUNNING", status_fn=ToilStatus.getStatus, process=wf, seconds=60)
456
- wf.wait()
457
- self.check_status("ERROR", status_fn=ToilStatus.getStatus, process=wf, seconds=60)
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
- def testGetStatusSuccessfulCWLWF(self):
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
- cmd = [
464
- "toil-cwl-runner",
465
- "--jobStore",
466
- self.toilDir,
467
- "--clean=never",
468
- "src/toil/test/cwl/sorttool.cwl",
469
- "--reverse",
470
- "--input",
471
- "src/toil/test/cwl/whale.txt",
472
- f"--outdir={self.tempDir}",
473
- ]
474
- wf = subprocess.Popen(cmd)
475
- self.check_status("RUNNING", status_fn=ToilStatus.getStatus, process=wf, seconds=60)
476
- wf.wait()
477
- self.check_status("COMPLETED", status_fn=ToilStatus.getStatus, process=wf, seconds=60)
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
- @patch("builtins.print")
481
- def testPrintJobLog(self, mock_print):
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
- cmd = [
485
- "toil-cwl-runner",
486
- "--logDebug",
487
- "--jobStore",
488
- self.toilDir,
489
- "--clean=never",
490
- "src/toil/test/cwl/alwaysfails.cwl",
491
- "--message",
492
- "Testing",
493
- ]
494
- logger.info("Run command: %s", " ".join(cmd))
495
- wf = subprocess.Popen(cmd)
496
- wf.wait()
497
- # print log and check output
498
- status = ToilStatus(self.toilDir)
499
- status.printJobLog()
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
- args, kwargs = mock_print.call_args
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 = self.restart_sort_workflow_cmd + ["--badWorker=1", "--logDebug"]
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 = self.restart_sort_workflow_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
- self.assertTrue(os.path.exists(self.toilDir))
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
- "file:" + self.toilDir,
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
- self.assertFalse(os.path.exists(self.toilDir))
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 __init__(self):
553
- Job.__init__(self)
554
-
555
- def run(self, fileStore):
629
+ def run(self, fileStore: AbstractFileStore) -> None:
556
630
  self.addChildFn(printUnicodeCharacter)