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
@@ -12,29 +12,32 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- import os
15
+ from pathlib import Path
16
16
 
17
17
  from toil.exceptions import FailedJobsException
18
- from toil.job import Job
18
+ from toil.job import Job, JobFunctionWrappingJob
19
19
  from toil.jobStores.abstractJobStore import NoSuchFileException
20
- from toil.test import ToilTest, slow
20
+ from toil.test import pslow as slow
21
21
 
22
+ import pytest
22
23
 
23
- class ResumabilityTest(ToilTest):
24
+
25
+ class TestResumability:
24
26
  """
25
27
  https://github.com/BD2KGenomics/toil/issues/808
26
28
  """
27
29
 
28
30
  @slow
29
- def test(self):
31
+ @pytest.mark.slow
32
+ def test(self, tmp_path: Path) -> None:
30
33
  """
31
34
  Tests that a toil workflow that fails once can be resumed without a NoSuchJobException.
32
35
  """
33
- options = Job.Runner.getDefaultOptions(self._getTestJobStorePath())
36
+ options = Job.Runner.getDefaultOptions(tmp_path / "jobstore")
34
37
  options.logLevel = "INFO"
35
38
  options.retryCount = 0
36
39
  root = Job.wrapJobFn(parent)
37
- with self.assertRaises(FailedJobsException):
40
+ with pytest.raises(FailedJobsException):
38
41
  # This one is intended to fail.
39
42
  Job.Runner.startToil(root, options)
40
43
 
@@ -44,30 +47,32 @@ class ResumabilityTest(ToilTest):
44
47
  # system code notices that the job has been deleted despite
45
48
  # the failure and avoids the failure.
46
49
  options.restart = True
47
- tempDir = self._createTempDir()
48
- options.logFile = os.path.join(tempDir, "log.txt")
50
+ tempDir = tmp_path / "tempdir"
51
+ tempDir.mkdir()
52
+ options.logFile = str(tempDir / "log.txt")
49
53
  Job.Runner.startToil(root, options)
50
54
  with open(options.logFile) as f:
51
55
  logString = f.read()
52
56
  # We are looking for e.g. "Batch system is reporting that
53
57
  # the jobGraph with batch system ID: 1 and jobGraph
54
58
  # store ID: n/t/jobwbijqL failed with exit value 1"
55
- self.assertTrue("failed with exit value" not in logString)
59
+ assert "failed with exit value" not in logString
56
60
 
57
- def test_chaining(self):
61
+ def test_chaining(self, tmp_path: Path) -> None:
58
62
  """
59
63
  Tests that a job which is chained to and fails can resume and succeed.
60
64
  """
61
65
 
62
- options = Job.Runner.getDefaultOptions(self._getTestJobStorePath())
66
+ options = Job.Runner.getDefaultOptions(tmp_path / "jobstore")
63
67
  options.logLevel = "DEBUG"
64
68
  options.retryCount = 0
65
- tempDir = self._createTempDir()
66
- options.logFile = os.path.join(tempDir, "log.txt")
69
+ tempDir = tmp_path / "tempdir"
70
+ tempDir.mkdir()
71
+ options.logFile = str(tempDir / "log.txt")
67
72
 
68
73
  root = Job.wrapJobFn(chaining_parent)
69
74
 
70
- with self.assertRaises(FailedJobsException):
75
+ with pytest.raises(FailedJobsException):
71
76
  # This one is intended to fail.
72
77
  Job.Runner.startToil(root, options)
73
78
 
@@ -83,7 +88,7 @@ class ResumabilityTest(ToilTest):
83
88
  Job.Runner.startToil(root, options)
84
89
 
85
90
 
86
- def parent(job):
91
+ def parent(job: Job) -> None:
87
92
  """
88
93
  Set up a bunch of dummy child jobs, and a bad job that needs to be
89
94
  restarted as the follow on.
@@ -93,21 +98,21 @@ def parent(job):
93
98
  job.addFollowOnJobFn(badChild)
94
99
 
95
100
 
96
- def chaining_parent(job):
101
+ def chaining_parent(job: Job) -> None:
97
102
  """
98
103
  Set up a failing job to chain to.
99
104
  """
100
105
  job.addFollowOnJobFn(badChild)
101
106
 
102
107
 
103
- def goodChild(job):
108
+ def goodChild(job: Job) -> None:
104
109
  """
105
110
  Does nothing.
106
111
  """
107
112
  return
108
113
 
109
114
 
110
- def badChild(job):
115
+ def badChild(job: JobFunctionWrappingJob) -> None:
111
116
  """
112
117
  Fails the first time it's run, succeeds the second time.
113
118
  """
@@ -11,93 +11,92 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
+ import argparse
15
+ from collections.abc import Callable
14
16
  import os
15
- import shutil
17
+ from pathlib import Path
16
18
 
17
19
  from toil.exceptions import FailedJobsException
18
- from toil.job import Job
19
- from toil.test import ToilTest
20
+ from toil.job import Job, JobFunctionWrappingJob
20
21
 
22
+ import pytest
21
23
 
22
- class CleanWorkDirTest(ToilTest):
24
+
25
+ class TestCleanWorkDir:
23
26
  """
24
27
  Tests testing :class:toil.fileStores.abstractFileStore.AbstractFileStore
25
28
  """
26
29
 
27
- def setUp(self):
28
- super().setUp()
29
- self.testDir = self._createTempDir()
30
-
31
- def tearDown(self):
32
- super().tearDown()
33
- shutil.rmtree(self.testDir)
34
-
35
- def testNever(self):
36
- retainedTempData = self._runAndReturnWorkDir("never", job=tempFileTestJob)
37
- self.assertNotEqual(
38
- retainedTempData,
39
- [],
30
+ def testNever(self, tmp_path: Path) -> None:
31
+ retainedTempData = self._runAndReturnWorkDir(
32
+ tmp_path, "never", job=tempFileTestJob
33
+ )
34
+ assert retainedTempData != [], (
40
35
  "The worker's temporary workspace was deleted despite "
41
- "cleanWorkDir being set to 'never'",
36
+ "cleanWorkDir being set to 'never'"
42
37
  )
43
38
 
44
- def testAlways(self):
45
- retainedTempData = self._runAndReturnWorkDir("always", job=tempFileTestJob)
46
- self.assertEqual(
47
- retainedTempData,
48
- [],
39
+ def testAlways(self, tmp_path: Path) -> None:
40
+ retainedTempData = self._runAndReturnWorkDir(
41
+ tmp_path, "always", job=tempFileTestJob
42
+ )
43
+ assert retainedTempData == [], (
49
44
  "The worker's temporary workspace was not deleted despite "
50
- "cleanWorkDir being set to 'always'",
45
+ "cleanWorkDir being set to 'always'"
51
46
  )
52
47
 
53
- def testOnErrorWithError(self):
48
+ def testOnErrorWithError(self, tmp_path: Path) -> None:
54
49
  retainedTempData = self._runAndReturnWorkDir(
55
- "onError", job=tempFileTestErrorJob, expectError=True
50
+ tmp_path, "onError", job=tempFileTestErrorJob, expectError=True
56
51
  )
57
- self.assertEqual(
58
- retainedTempData,
59
- [],
52
+ assert retainedTempData == [], (
60
53
  "The worker's temporary workspace was not deleted despite "
61
- "an error occurring and cleanWorkDir being set to 'onError'",
54
+ "an error occurring and cleanWorkDir being set to 'onError'"
62
55
  )
63
56
 
64
- def testOnErrorWithNoError(self):
65
- retainedTempData = self._runAndReturnWorkDir("onError", job=tempFileTestJob)
66
- self.assertNotEqual(
67
- retainedTempData,
68
- [],
57
+ def testOnErrorWithNoError(self, tmp_path: Path) -> None:
58
+ retainedTempData = self._runAndReturnWorkDir(
59
+ tmp_path, "onError", job=tempFileTestJob
60
+ )
61
+ assert retainedTempData != [], (
69
62
  "The worker's temporary workspace was deleted despite "
70
- "no error occurring and cleanWorkDir being set to 'onError'",
63
+ "no error occurring and cleanWorkDir being set to 'onError'"
71
64
  )
72
65
 
73
- def testOnSuccessWithError(self):
66
+ def testOnSuccessWithError(self, tmp_path: Path) -> None:
74
67
  retainedTempData = self._runAndReturnWorkDir(
75
- "onSuccess", job=tempFileTestErrorJob, expectError=True
68
+ tmp_path, "onSuccess", job=tempFileTestErrorJob, expectError=True
76
69
  )
77
- self.assertNotEqual(
78
- retainedTempData,
79
- [],
70
+ assert retainedTempData != [], (
80
71
  "The worker's temporary workspace was deleted despite "
81
- "an error occurring and cleanWorkDir being set to 'onSuccesss'",
72
+ "an error occurring and cleanWorkDir being set to 'onSuccesss'"
82
73
  )
83
74
 
84
- def testOnSuccessWithSuccess(self):
85
- retainedTempData = self._runAndReturnWorkDir("onSuccess", job=tempFileTestJob)
86
- self.assertEqual(
87
- retainedTempData,
88
- [],
75
+ def testOnSuccessWithSuccess(self, tmp_path: Path) -> None:
76
+ retainedTempData = self._runAndReturnWorkDir(
77
+ tmp_path, "onSuccess", job=tempFileTestJob
78
+ )
79
+ assert retainedTempData == [], (
89
80
  "The worker's temporary workspace was not deleted despite "
90
- "a successful job execution and cleanWorkDir being set to 'onSuccesss'",
81
+ "a successful job execution and cleanWorkDir being set to 'onSuccesss'"
91
82
  )
92
83
 
93
- def _runAndReturnWorkDir(self, cleanWorkDir, job, expectError=False):
84
+ def _runAndReturnWorkDir(
85
+ self,
86
+ tmp_path: Path,
87
+ cleanWorkDir: str,
88
+ job: Callable[[JobFunctionWrappingJob], None],
89
+ expectError: bool = False,
90
+ ) -> list[str]:
94
91
  """
95
92
  Runs toil with the specified job and cleanWorkDir setting. expectError determines whether the test's toil
96
93
  run is expected to succeed, and the test will fail if that expectation is not met. returns the contents of
97
94
  the workDir after completion of the run
98
95
  """
99
- options = Job.Runner.getDefaultOptions(self._getTestJobStorePath())
100
- options.workDir = self.testDir
96
+ workdir = tmp_path / "testDir"
97
+ workdir.mkdir()
98
+ options = Job.Runner.getDefaultOptions(tmp_path / "jobstore")
99
+ options.workDir = str(workdir)
101
100
  options.clean = "always"
102
101
  options.cleanWorkDir = cleanWorkDir
103
102
  A = Job.wrapJobFn(job)
@@ -105,26 +104,30 @@ class CleanWorkDirTest(ToilTest):
105
104
  self._launchError(A, options)
106
105
  else:
107
106
  self._launchRegular(A, options)
108
- return os.listdir(self.testDir)
107
+ return os.listdir(workdir)
109
108
 
110
- def _launchRegular(self, A, options):
109
+ def _launchRegular(
110
+ self, A: JobFunctionWrappingJob, options: argparse.Namespace
111
+ ) -> None:
111
112
  Job.Runner.startToil(A, options)
112
113
 
113
- def _launchError(self, A, options):
114
+ def _launchError(
115
+ self, A: JobFunctionWrappingJob, options: argparse.Namespace
116
+ ) -> None:
114
117
  try:
115
118
  Job.Runner.startToil(A, options)
116
119
  except FailedJobsException:
117
120
  pass # we expect a job to fail here
118
121
  else:
119
- self.fail("Toil run succeeded unexpectedly")
122
+ pytest.fail("Toil run succeeded unexpectedly")
120
123
 
121
124
 
122
- def tempFileTestJob(job):
125
+ def tempFileTestJob(job: JobFunctionWrappingJob) -> None:
123
126
  with open(job.fileStore.getLocalTempFile(), "w") as f:
124
127
  f.write("test file retention")
125
128
 
126
129
 
127
- def tempFileTestErrorJob(job):
130
+ def tempFileTestErrorJob(job: JobFunctionWrappingJob) -> None:
128
131
  with open(job.fileStore.getLocalTempFile(), "w") as f:
129
132
  f.write("test file retention")
130
133
  raise RuntimeError() # test failure
@@ -1,46 +1,50 @@
1
1
  import errno
2
2
  import multiprocessing
3
3
  import os
4
+ from pathlib import Path
4
5
  from functools import partial
6
+ from typing import Any, Optional
5
7
 
6
8
  from toil.lib.io import mkdtemp
7
9
  from toil.lib.threading import cpu_count
8
- from toil.test import ToilTest
9
10
 
10
11
 
11
- class SystemTest(ToilTest):
12
+ class TestSystem:
12
13
  """Test various assumptions about the operating system's behavior."""
13
14
 
14
- def testAtomicityOfNonEmptyDirectoryRenames(self):
15
+ def testAtomicityOfNonEmptyDirectoryRenames(self, tmp_path: Path) -> None:
15
16
  for _ in range(100):
16
- parent = self._createTempDir(purpose="parent")
17
- child = os.path.join(parent, "child")
17
+ parent = tmp_path / f"parent{_}"
18
+ parent.mkdir()
19
+ child = parent / "child"
18
20
  # Use processes (as opposed to threads) to prevent GIL from ordering things artificially
19
21
  pool = multiprocessing.Pool(processes=cpu_count())
20
22
  try:
21
23
  numTasks = cpu_count() * 10
22
- grandChildIds = pool.map_async(
24
+ temp_grandChildIds = pool.map_async(
23
25
  func=partial(
24
26
  _testAtomicityOfNonEmptyDirectoryRenamesTask, parent, child
25
27
  ),
26
28
  iterable=list(range(numTasks)),
27
29
  )
28
- grandChildIds = grandChildIds.get()
30
+ grandChildIds = temp_grandChildIds.get()
29
31
  finally:
30
32
  pool.close()
31
33
  pool.join()
32
- self.assertEqual(len(grandChildIds), numTasks)
34
+ assert len(grandChildIds) == numTasks
33
35
  # Assert that we only had one winner
34
36
  grandChildIds = [n for n in grandChildIds if n is not None]
35
- self.assertEqual(len(grandChildIds), 1)
37
+ assert len(grandChildIds) == 1
36
38
  # Assert that the winner's grandChild wasn't silently overwritten by a looser
37
39
  expectedGrandChildId = grandChildIds[0]
38
- actualGrandChild = os.path.join(child, "grandChild")
39
- actualGrandChildId = os.stat(actualGrandChild).st_ino
40
- self.assertEqual(actualGrandChildId, expectedGrandChildId)
40
+ actualGrandChild = child / "grandChild"
41
+ actualGrandChildId = actualGrandChild.stat().st_ino
42
+ assert actualGrandChildId == expectedGrandChildId
41
43
 
42
44
 
43
- def _testAtomicityOfNonEmptyDirectoryRenamesTask(parent, child, _):
45
+ def _testAtomicityOfNonEmptyDirectoryRenamesTask(
46
+ parent: Path, child: Path, _: Any
47
+ ) -> Optional[int]:
44
48
  tmpChildDir = mkdtemp(dir=parent, prefix="child", suffix=".tmp")
45
49
  grandChild = os.path.join(tmpChildDir, "grandChild")
46
50
  open(grandChild, "w").close()
@@ -1,100 +1,100 @@
1
1
  import logging
2
2
  import multiprocessing
3
3
  import os
4
+ from pathlib import Path
4
5
  import random
5
6
  import time
6
7
  import traceback
7
8
  from functools import partial
8
9
 
9
10
  from toil.lib.threading import LastProcessStandingArena, cpu_count, global_mutex
10
- from toil.test import ToilTest
11
11
 
12
12
  log = logging.getLogger(__name__)
13
13
 
14
14
 
15
- class ThreadingTest(ToilTest):
15
+ class TestThreading:
16
16
  """Test Toil threading/synchronization tools."""
17
17
 
18
- def testGlobalMutexOrdering(self):
18
+ def testGlobalMutexOrdering(self, tmp_path: Path) -> None:
19
19
  for it in range(10):
20
20
  log.info("Iteration %d", it)
21
21
 
22
- scope = self._createTempDir()
22
+ scope = tmp_path / f"tempDir{it}"
23
+ scope.mkdir()
23
24
  mutex = "mutex"
24
25
  # Use processes (as opposed to threads) to prevent GIL from ordering things artificially
25
26
  pool = multiprocessing.Pool(processes=cpu_count())
26
27
  try:
27
28
  numTasks = 100
28
- results = pool.map_async(
29
+ temp_results = pool.map_async(
29
30
  func=partial(_testGlobalMutexOrderingTask, scope, mutex),
30
31
  iterable=list(range(numTasks)),
31
32
  )
32
- results = results.get()
33
+ results = temp_results.get()
33
34
  finally:
34
35
  pool.close()
35
36
  pool.join()
36
37
 
37
- self.assertEqual(len(results), numTasks)
38
+ assert len(results) == numTasks
38
39
  for item in results:
39
40
  # Make sure all workers say they succeeded
40
- self.assertEqual(item, True)
41
+ assert item is True
41
42
 
42
- def testLastProcessStanding(self):
43
+ def testLastProcessStanding(self, tmp_path: Path) -> None:
43
44
  for it in range(10):
44
45
  log.info("Iteration %d", it)
45
46
 
46
- scope = self._createTempDir()
47
+ scope = tmp_path / f"tempDir{it}"
48
+ scope.mkdir()
47
49
  arena_name = "thunderdome"
48
50
  # Use processes (as opposed to threads) to prevent GIL from ordering things artificially
49
51
  pool = multiprocessing.Pool(processes=cpu_count())
50
52
  try:
51
53
  numTasks = 100
52
- results = pool.map_async(
54
+ temp_results = pool.map_async(
53
55
  func=partial(_testLastProcessStandingTask, scope, arena_name),
54
56
  iterable=list(range(numTasks)),
55
57
  )
56
- results = results.get()
58
+ results = temp_results.get()
57
59
  finally:
58
60
  pool.close()
59
61
  pool.join()
60
62
 
61
- self.assertEqual(len(results), numTasks)
63
+ assert len(results) == numTasks
62
64
  for item in results:
63
65
  # Make sure all workers say they succeeded
64
- self.assertEqual(item, True)
66
+ assert item is True
65
67
  for filename in os.listdir(scope):
66
68
  assert not filename.startswith(
67
69
  "precious"
68
70
  ), f"File {filename} still exists"
69
71
 
70
72
 
71
- def _testGlobalMutexOrderingTask(scope, mutex, number):
73
+ def _testGlobalMutexOrderingTask(scope: Path, mutex: str, number: int) -> bool:
72
74
  try:
73
75
  # We will all fight over the potato
74
- potato = os.path.join(scope, "potato")
76
+ potato = scope / "potato"
75
77
 
76
78
  with global_mutex(scope, mutex):
77
79
  log.info("PID %d = num %d running", os.getpid(), number)
78
- assert not os.path.exists(
79
- potato
80
- ), "We see someone else holding the potato file"
80
+ assert not potato.exists(), "We see someone else holding the potato file"
81
81
 
82
82
  # Put our name there
83
- with open(potato, "w") as out_stream:
83
+ with potato.open("w") as out_stream:
84
84
  out_stream.write(str(number))
85
85
 
86
86
  # Wait
87
87
  time.sleep(random.random() * 0.01)
88
88
 
89
89
  # Make sure our name is still there
90
- with open(potato) as in_stream:
90
+ with potato.open() as in_stream:
91
91
  seen = in_stream.read().rstrip()
92
92
  assert seen == str(
93
93
  number
94
94
  ), f"We are {number} but {seen} stole our potato!"
95
95
 
96
- os.unlink(potato)
97
- assert not os.path.exists(potato), "We left the potato behind"
96
+ potato.unlink()
97
+ assert not potato.exists(), "We left the potato behind"
98
98
  log.info("PID %d = num %d dropped potato", os.getpid(), number)
99
99
  return True
100
100
  except:
@@ -102,7 +102,7 @@ def _testGlobalMutexOrderingTask(scope, mutex, number):
102
102
  return False
103
103
 
104
104
 
105
- def _testLastProcessStandingTask(scope, arena_name, number):
105
+ def _testLastProcessStandingTask(scope: Path, arena_name: str, number: int) -> bool:
106
106
  try:
107
107
  arena = LastProcessStandingArena(scope, arena_name)
108
108
 
@@ -110,20 +110,18 @@ def _testLastProcessStandingTask(scope, arena_name, number):
110
110
  log.info("PID %d = num %d entered arena", os.getpid(), number)
111
111
  try:
112
112
  # We all make files
113
- my_precious = os.path.join(scope, "precious" + str(number))
113
+ my_precious = scope / f"precious{number}"
114
114
 
115
115
  # Put our name there
116
- with open(my_precious, "w") as out_stream:
116
+ with my_precious.open("w") as out_stream:
117
117
  out_stream.write(str(number))
118
118
 
119
119
  # Wait
120
120
  time.sleep(random.random() * 0.01)
121
121
 
122
122
  # Make sure our file is still there unmodified
123
- assert os.path.exists(
124
- my_precious
125
- ), f"Precious file {my_precious} has been stolen!"
126
- with open(my_precious) as in_stream:
123
+ assert my_precious.exists(), f"Precious file {my_precious} has been stolen!"
124
+ with my_precious.open() as in_stream:
127
125
  seen = in_stream.read().rstrip()
128
126
  assert seen == str(
129
127
  number
@@ -131,7 +129,6 @@ def _testLastProcessStandingTask(scope, arena_name, number):
131
129
  finally:
132
130
  was_last = False
133
131
  for _ in arena.leave():
134
- was_last = True
135
132
  log.info("PID %d = num %d is last standing", os.getpid(), number)
136
133
 
137
134
  # Clean up all the files
@@ -143,7 +140,7 @@ def _testLastProcessStandingTask(scope, arena_name, number):
143
140
  number,
144
141
  filename,
145
142
  )
146
- os.unlink(os.path.join(scope, filename))
143
+ (scope / filename).unlink()
147
144
 
148
145
  log.info("PID %d = num %d left arena", os.getpid(), number)
149
146
 
@@ -0,0 +1 @@
1
+ B