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
@@ -13,33 +13,34 @@
13
13
  # limitations under the License.
14
14
  import codecs
15
15
  import logging
16
- import os
16
+ from pathlib import Path
17
17
  import random
18
18
  import sys
19
19
  import time
20
20
  import traceback
21
+ from typing import cast, Any, Literal
21
22
  from threading import Event, Thread
22
- from unittest import skipIf
23
23
 
24
24
  import pytest
25
25
 
26
26
  from toil.batchSystems.singleMachine import SingleMachineBatchSystem
27
27
  from toil.exceptions import FailedJobsException
28
- from toil.job import Job
29
- from toil.leader import DeadlockException
30
- from toil.lib.retry import retry_flaky_test
31
- from toil.test import ToilTest, get_temp_file, slow
28
+ from toil.job import Job, ServiceHostJob
29
+ from toil.jobStores.abstractJobStore import AbstractJobStore
30
+ from toil.batchSystems import DeadlockException
31
+ from toil.test import pslow as slow
32
32
 
33
33
  logger = logging.getLogger(__name__)
34
34
 
35
35
 
36
- class JobServiceTest(ToilTest):
36
+ class TestJobService:
37
37
  """
38
38
  Tests testing the Job.Service class
39
39
  """
40
40
 
41
41
  @slow
42
- def testServiceSerialization(self):
42
+ @pytest.mark.slow
43
+ def testServiceSerialization(self, tmp_path: Path) -> None:
43
44
  """
44
45
  Tests that a service can receive a promise without producing a serialization
45
46
  error.
@@ -47,114 +48,119 @@ class JobServiceTest(ToilTest):
47
48
  job = Job()
48
49
  service = ToySerializableService("woot")
49
50
  startValue = job.addService(service) # Add a first service to job
50
- subService = ToySerializableService(startValue) # Now create a child of
51
+ subService = ToySerializableService(
52
+ cast(str, startValue)
53
+ ) # Now create a child of
51
54
  # that service that takes the start value promise from the parent service
52
55
  job.addService(subService, parentService=service) # This should work if
53
56
  # serialization on services is working correctly.
54
57
 
55
- self.runToil(job)
58
+ self.runToil(tmp_path, job)
56
59
 
57
60
  @slow
58
- def testService(self, checkpoint=False):
61
+ @pytest.mark.slow
62
+ def testService(self, tmp_path: Path, checkpoint: bool = False) -> None:
59
63
  """
60
64
  Tests the creation of a Job.Service with random failures of the worker.
61
65
  """
62
66
  for test in range(2):
63
- outFile = get_temp_file(rootDir=self._createTempDir()) # Temporary file
67
+ outFile = tmp_path / f"test{test}"
64
68
  messageInt = random.randint(1, sys.maxsize)
65
- try:
66
- # Wire up the services/jobs
67
- t = Job.wrapJobFn(
68
- serviceTest, outFile, messageInt, checkpoint=checkpoint
69
- )
69
+ # Wire up the services/jobs
70
+ t = Job.wrapJobFn(serviceTest, outFile, messageInt, checkpoint=checkpoint)
70
71
 
71
- # Run the workflow repeatedly until success
72
- self.runToil(t)
72
+ # Run the workflow repeatedly until success
73
+ sub_tmp_path = tmp_path / str(test)
74
+ sub_tmp_path.mkdir()
75
+ self.runToil(sub_tmp_path, t)
73
76
 
74
- # Check output
75
- self.assertEqual(int(open(outFile).readline()), messageInt)
76
- finally:
77
- os.remove(outFile)
77
+ # Check output
78
+ assert int(outFile.read_text()) == messageInt
78
79
 
79
80
  @slow
80
- @skipIf(
81
+ @pytest.mark.slow
82
+ @pytest.mark.skipif(
81
83
  SingleMachineBatchSystem.numCores < 4,
82
- "Need at least four cores to run this test",
84
+ reason="Need at least four cores to run this test",
83
85
  )
84
- def testServiceDeadlock(self):
86
+ def testServiceDeadlock(self, tmp_path: Path) -> None:
85
87
  """
86
88
  Creates a job with more services than maxServices, checks that deadlock is detected.
87
89
  """
88
- outFile = get_temp_file(rootDir=self._createTempDir())
89
- try:
90
+ outFile = tmp_path / "out"
91
+
92
+ def makeWorkflow() -> Job:
93
+ job = Job()
94
+ r1 = job.addService(ToySerializableService("woot1"))
95
+ r2 = job.addService(ToySerializableService("woot2"))
96
+ r3 = job.addService(ToySerializableService("woot3"))
97
+ job.addChildFn(fnTest, [r1, r2, r3], outFile)
98
+ return job
99
+
100
+ # This should fail as too few services available
101
+ sub_tmp_path1 = tmp_path / "1"
102
+ sub_tmp_path1.mkdir()
103
+ with pytest.raises(DeadlockException):
104
+ self.runToil(
105
+ sub_tmp_path1,
106
+ makeWorkflow(),
107
+ badWorker=0.0,
108
+ maxServiceJobs=2,
109
+ deadlockWait=5,
110
+ )
90
111
 
91
- def makeWorkflow():
92
- job = Job()
93
- r1 = job.addService(ToySerializableService("woot1"))
94
- r2 = job.addService(ToySerializableService("woot2"))
95
- r3 = job.addService(ToySerializableService("woot3"))
96
- job.addChildFn(fnTest, [r1, r2, r3], outFile)
97
- return job
112
+ # This should pass, as adequate services available
113
+ sub_tmp_path2 = tmp_path / "2"
114
+ sub_tmp_path2.mkdir()
115
+ self.runToil(sub_tmp_path2, makeWorkflow(), maxServiceJobs=3)
116
+ # Check we get expected output
117
+ assert outFile.read_text() == "woot1 woot2 woot3"
98
118
 
99
- # This should fail as too few services available
100
- try:
101
- self.runToil(
102
- makeWorkflow(), badWorker=0.0, maxServiceJobs=2, deadlockWait=5
103
- )
104
- except DeadlockException:
105
- print("Got expected deadlock exception")
106
- else:
107
- assert 0
108
-
109
- # This should pass, as adequate services available
110
- self.runToil(makeWorkflow(), maxServiceJobs=3)
111
- # Check we get expected output
112
- assert open(outFile).read() == "woot1 woot2 woot3"
113
- finally:
114
- os.remove(outFile)
115
-
116
- def testServiceWithCheckpoints(self):
119
+ def testServiceWithCheckpoints(self, tmp_path: Path) -> None:
117
120
  """
118
121
  Tests the creation of a Job.Service with random failures of the worker, making the root job use checkpointing to
119
122
  restart the subtree.
120
123
  """
121
- self.testService(checkpoint=True)
124
+ self.testService(tmp_path, checkpoint=True)
122
125
 
123
126
  @slow
124
- @skipIf(
127
+ @pytest.mark.slow
128
+ @pytest.mark.skipif(
125
129
  SingleMachineBatchSystem.numCores < 4,
126
- "Need at least four cores to run this test",
130
+ reason="Need at least four cores to run this test",
127
131
  )
128
- def testServiceRecursive(self, checkpoint=True):
132
+ def testServiceRecursive(self, tmp_path: Path, checkpoint: bool = True) -> None:
129
133
  """
130
134
  Tests the creation of a Job.Service, creating a chain of services and accessing jobs.
131
135
  Randomly fails the worker.
132
136
  """
133
137
  for test in range(1):
134
138
  # Temporary file
135
- outFile = get_temp_file(rootDir=self._createTempDir())
139
+ outFile = tmp_path / f"test{test}"
136
140
  messages = [random.randint(1, sys.maxsize) for i in range(3)]
137
- try:
138
- # Wire up the services/jobs
139
- t = Job.wrapJobFn(
140
- serviceTestRecursive, outFile, messages, checkpoint=checkpoint
141
- )
141
+ # Wire up the services/jobs
142
+ t = Job.wrapJobFn(
143
+ serviceTestRecursive, outFile, messages, checkpoint=checkpoint
144
+ )
142
145
 
143
- # Run the workflow repeatedly until success
144
- self.runToil(t)
146
+ # Run the workflow repeatedly until success
147
+ sub_tmp_path = tmp_path / str(test)
148
+ sub_tmp_path.mkdir()
149
+ self.runToil(sub_tmp_path, t)
145
150
 
146
- # Check output
147
- self.assertEqual(list(map(int, open(outFile).readlines())), messages)
148
- finally:
149
- os.remove(outFile)
151
+ # Check output
152
+ assert list(map(int, outFile.open().readlines())) == messages
150
153
 
151
154
  @slow
152
- @skipIf(
155
+ @pytest.mark.slow
156
+ @pytest.mark.skipif(
153
157
  SingleMachineBatchSystem.numCores < 4,
154
- "Need at least four cores to run this test",
158
+ reason="Need at least four cores to run this test",
155
159
  )
156
160
  @pytest.mark.timeout(1200)
157
- def testServiceParallelRecursive(self, checkpoint=True):
161
+ def testServiceParallelRecursive(
162
+ self, tmp_path: Path, checkpoint: bool = True
163
+ ) -> None:
158
164
  """
159
165
  Tests the creation of a Job.Service, creating parallel chains of services and accessing jobs.
160
166
  Randomly fails the worker.
@@ -176,51 +182,54 @@ class JobServiceTest(ToilTest):
176
182
  logger.info("This test will fail spuriously with probability %s", p_test_failure)
177
183
 
178
184
  # We want to run the workflow through several times to test restarting, so we need it to often fail but reliably sometimes succeed, and almost always succeed when repeated.
179
-
180
- self.assertGreater(0.8, p_workflow_success)
181
- self.assertGreater(p_workflow_success, 0.2)
182
- self.assertGreater(0.001, p_test_failure)
183
-
185
+
186
+ assert 0.8 > p_workflow_success
187
+ assert p_workflow_success > 0.2
188
+ assert 0.001 > p_test_failure
184
189
 
185
190
  for test in range(1):
186
191
  # Temporary file
187
- outFiles = [get_temp_file(rootDir=self._createTempDir()) for j in range(BUNDLE_COUNT)]
192
+ outFiles = [tmp_path / f"test{test}_{j}" for j in range(BUNDLE_COUNT)]
188
193
  # We send 3 messages each in 2 sets, each of which needs a service and a client
189
194
  messageBundles = [
190
195
  [random.randint(1, sys.maxsize) for i in range(BUNDLE_SIZE)] for j in range(BUNDLE_COUNT)
191
196
  ]
192
- try:
193
- # Wire up the services/jobs
194
- t = Job.wrapJobFn(
195
- serviceTestParallelRecursive,
196
- outFiles,
197
- messageBundles,
198
- checkpoint=True,
199
- )
197
+ # Wire up the services/jobs
198
+ t = Job.wrapJobFn(
199
+ serviceTestParallelRecursive,
200
+ outFiles,
201
+ messageBundles,
202
+ checkpoint=True,
203
+ )
200
204
 
201
- # Run the workflow repeatedly until success
202
- self.runToil(t, retryCount=RETRY_COUNT, badWorker=FAIL_FRACTION, max_attempts=MAX_ATTEMPTS)
205
+ # Run the workflow repeatedly until success
206
+ sub_tmp_path = tmp_path / str(test)
207
+ sub_tmp_path.mkdir()
208
+ self.runToil(
209
+ sub_tmp_path,
210
+ t,
211
+ retryCount=RETRY_COUNT,
212
+ badWorker=FAIL_FRACTION,
213
+ max_attempts=MAX_ATTEMPTS,
214
+ )
203
215
 
204
- # Check output
205
- for messages, outFile in zip(messageBundles, outFiles):
206
- self.assertEqual(
207
- list(map(int, open(outFile).readlines())), messages
208
- )
209
- finally:
210
- list(map(os.remove, outFiles))
216
+ # Check output
217
+ for messages, outFile in zip(messageBundles, outFiles):
218
+ assert list(map(int, outFile.open().readlines())) == messages
211
219
 
212
220
  def runToil(
213
221
  self,
214
- rootJob,
215
- retryCount=1,
216
- badWorker=0.5,
217
- badWorkedFailInterval=0.1,
218
- maxServiceJobs=sys.maxsize,
219
- deadlockWait=60,
220
- max_attempts=50
221
- ):
222
+ tmp_path: Path,
223
+ rootJob: Job,
224
+ retryCount: int = 1,
225
+ badWorker: float = 0.5,
226
+ badWorkedFailInterval: float = 0.1,
227
+ maxServiceJobs: int = sys.maxsize,
228
+ deadlockWait: int = 60,
229
+ max_attempts: int = 50,
230
+ ) -> None:
222
231
  # Create the runner for the workflow.
223
- options = Job.Runner.getDefaultOptions(self._getTestJobStorePath())
232
+ options = Job.Runner.getDefaultOptions(tmp_path / "jobstore")
224
233
  options.logLevel = "DEBUG"
225
234
 
226
235
  options.retryCount = retryCount
@@ -240,18 +249,14 @@ class JobServiceTest(ToilTest):
240
249
  i = e.numberOfFailedJobs
241
250
  logger.info("Workflow attempt %s/%s failed with %s failed jobs", total_tries, max_attempts, i)
242
251
  if total_tries == max_attempts:
243
- self.fail() # Exceeded a reasonable number of restarts
252
+ pytest.fail(reason="Exceeded a reasonable number of restarts")
244
253
  total_tries += 1
245
254
  options.restart = True
246
255
  logger.info("Succeeded after %s/%s attempts", total_tries, max_attempts)
247
256
 
248
257
 
249
- class PerfectServiceTest(JobServiceTest):
250
- def runToil(
251
- self,
252
- *args,
253
- **kwargs
254
- ):
258
+ class TestPerfectServicet(TestJobService):
259
+ def runToil(self, *args: Any, **kwargs: Any) -> None:
255
260
  """
256
261
  Let us run all the tests in the other service test class, but without worker failures.
257
262
  """
@@ -262,13 +267,13 @@ class PerfectServiceTest(JobServiceTest):
262
267
  )
263
268
 
264
269
 
265
- def serviceTest(job, outFile, messageInt):
270
+ def serviceTest(job: Job, outFile: Path, messageInt: int) -> None:
266
271
  """
267
272
  Creates one service and one accessing job, which communicate with two files to establish
268
273
  that both run concurrently.
269
274
  """
270
275
  # Clean out out-file
271
- open(outFile, "w").close()
276
+ outFile.open("w").close()
272
277
  # We create a random number that is added to messageInt and subtracted by
273
278
  # the serviceAccessor, to prove that when service test is checkpointed and
274
279
  # restarted there is never a connection made between an earlier service and
@@ -284,13 +289,13 @@ def serviceTest(job, outFile, messageInt):
284
289
  )
285
290
 
286
291
 
287
- def serviceTestRecursive(job, outFile, messages):
292
+ def serviceTestRecursive(job: Job, outFile: Path, messages: list[int]) -> None:
288
293
  """
289
294
  Creates a chain of services and accessing jobs, each paired together.
290
295
  """
291
296
  if len(messages) > 0:
292
297
  # Clean out out-file
293
- open(outFile, "w").close()
298
+ outFile.open("w").close()
294
299
  to_add = random.randint(1, sys.maxsize)
295
300
  service = ToyService(messages[0] + to_add)
296
301
  child = job.addChildJobFn(
@@ -310,13 +315,15 @@ def serviceTestRecursive(job, outFile, messages):
310
315
  service = service2
311
316
 
312
317
 
313
- def serviceTestParallelRecursive(job, outFiles, messageBundles):
318
+ def serviceTestParallelRecursive(
319
+ job: Job, outFiles: list[Path], messageBundles: list[list[int]]
320
+ ) -> None:
314
321
  """
315
322
  Creates multiple chains of services and accessing jobs.
316
323
  """
317
324
  for messages, outFile in zip(messageBundles, outFiles):
318
325
  # Clean out out-file
319
- open(outFile, "w").close()
326
+ outFile.open("w").close()
320
327
  if len(messages) > 0:
321
328
  to_add = random.randint(1, sys.maxsize)
322
329
  service = ToyService(messages[0] + to_add)
@@ -338,7 +345,7 @@ def serviceTestParallelRecursive(job, outFiles, messageBundles):
338
345
 
339
346
 
340
347
  class ToyService(Job.Service):
341
- def __init__(self, messageInt, *args, **kwargs):
348
+ def __init__(self, messageInt: int, *args: Any, **kwargs: Any) -> None:
342
349
  """
343
350
  While established the service repeatedly:
344
351
  - reads an integer i from the inJobStoreFileID file
@@ -347,7 +354,7 @@ class ToyService(Job.Service):
347
354
  Job.Service.__init__(self, *args, **kwargs)
348
355
  self.messageInt = messageInt
349
356
 
350
- def start(self, job):
357
+ def start(self, job: ServiceHostJob) -> tuple[str, str]:
351
358
  assert self.disk is not None
352
359
  assert self.memory is not None
353
360
  assert self.cores is not None
@@ -371,19 +378,24 @@ class ToyService(Job.Service):
371
378
  self.serviceThread.start()
372
379
  return (inJobStoreID, outJobStoreID)
373
380
 
374
- def stop(self, job):
381
+ def stop(self, job: Job) -> None:
375
382
  self.terminate.set()
376
383
  self.serviceThread.join()
377
384
 
378
- def check(self):
385
+ def check(self) -> Literal[True]:
379
386
  if self.error.isSet():
380
387
  raise RuntimeError("Service worker failed")
381
388
  return True
382
389
 
383
390
  @staticmethod
384
391
  def serviceWorker(
385
- jobStore, terminate, error, inJobStoreID, outJobStoreID, messageInt
386
- ):
392
+ jobStore: AbstractJobStore,
393
+ terminate: Event,
394
+ error: Event,
395
+ inJobStoreID: str,
396
+ outJobStoreID: str,
397
+ messageInt: int,
398
+ ) -> None:
387
399
  try:
388
400
  while True:
389
401
  if terminate.isSet(): # Quit if we've got the terminate signal
@@ -395,8 +407,7 @@ class ToyService(Job.Service):
395
407
  # Try reading a line from the input file
396
408
  try:
397
409
  with jobStore.read_file_stream(inJobStoreID) as f:
398
- f = codecs.getreader("utf-8")(f)
399
- line = f.readline()
410
+ line = codecs.getreader("utf-8")(f).readline()
400
411
  except:
401
412
  logger.debug(
402
413
  "Something went wrong reading a line: %s",
@@ -428,7 +439,12 @@ class ToyService(Job.Service):
428
439
  raise
429
440
 
430
441
 
431
- def serviceAccessor(job, communicationFiles, outFile, to_subtract):
442
+ def serviceAccessor(
443
+ job: ServiceHostJob,
444
+ communicationFiles: tuple[str, str],
445
+ outFile: Path,
446
+ to_subtract: int,
447
+ ) -> None:
432
448
  """
433
449
  Writes a random integer iinto the inJobStoreFileID file, then tries 10 times reading
434
450
  from outJobStoreFileID to get a pair of integers, the first equal to i the second written into the outputFile.
@@ -462,7 +478,7 @@ def serviceAccessor(job, communicationFiles, outFile, to_subtract):
462
478
  logger.debug(
463
479
  f"Matched key's: {key}, writing message: {int(message) - to_subtract} with to_subtract: {to_subtract}"
464
480
  )
465
- with open(outFile, "a") as fH:
481
+ with outFile.open("a") as fH:
466
482
  fH.write("%s\n" % (int(message) - to_subtract))
467
483
  return
468
484
 
@@ -470,26 +486,26 @@ def serviceAccessor(job, communicationFiles, outFile, to_subtract):
470
486
 
471
487
 
472
488
  class ToySerializableService(Job.Service):
473
- def __init__(self, messageInt, *args, **kwargs):
489
+ def __init__(self, messageInt: str, *args: Any, **kwargs: Any) -> None:
474
490
  """
475
491
  Trivial service for testing serialization.
476
492
  """
477
493
  Job.Service.__init__(self, *args, **kwargs)
478
494
  self.messageInt = messageInt
479
495
 
480
- def start(self, job):
496
+ def start(self, job: Job) -> str:
481
497
  return self.messageInt
482
498
 
483
- def stop(self, job):
499
+ def stop(self, job: Job) -> None:
484
500
  pass
485
501
 
486
- def check(self):
502
+ def check(self) -> Literal[True]:
487
503
  return True
488
504
 
489
505
 
490
- def fnTest(strings, outputFile):
506
+ def fnTest(strings: list[str], outputFile: Path) -> None:
491
507
  """
492
508
  Function concatenates the strings together and writes them to the output file
493
509
  """
494
- with open(outputFile, "w") as fH:
510
+ with outputFile.open("w") as fH:
495
511
  fH.write(" ".join(strings))