toil 8.1.0b1__py3-none-any.whl → 9.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (275) 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/registry.py +15 -118
  11. toil/batchSystems/singleMachine.py +1 -1
  12. toil/batchSystems/slurm.py +27 -26
  13. toil/bus.py +5 -3
  14. toil/common.py +59 -12
  15. toil/cwl/cwltoil.py +81 -38
  16. toil/cwl/utils.py +103 -3
  17. toil/job.py +64 -49
  18. toil/jobStores/abstractJobStore.py +35 -239
  19. toil/jobStores/aws/jobStore.py +2 -1
  20. toil/jobStores/fileJobStore.py +27 -2
  21. toil/jobStores/googleJobStore.py +110 -33
  22. toil/leader.py +9 -0
  23. toil/lib/accelerators.py +4 -2
  24. toil/lib/aws/utils.py.orig +504 -0
  25. toil/lib/bioio.py +1 -1
  26. toil/lib/docker.py +252 -91
  27. toil/lib/dockstore.py +11 -3
  28. toil/lib/exceptions.py +5 -3
  29. toil/lib/generatedEC2Lists.py +81 -19
  30. toil/lib/history.py +87 -13
  31. toil/lib/history_submission.py +23 -9
  32. toil/lib/io.py +34 -22
  33. toil/lib/misc.py +8 -2
  34. toil/lib/plugins.py +106 -0
  35. toil/lib/resources.py +2 -1
  36. toil/lib/threading.py +11 -10
  37. toil/lib/url.py +320 -0
  38. toil/options/common.py +8 -0
  39. toil/options/cwl.py +13 -1
  40. toil/options/runner.py +17 -10
  41. toil/options/wdl.py +22 -0
  42. toil/provisioners/aws/awsProvisioner.py +25 -2
  43. toil/server/api_spec/LICENSE +201 -0
  44. toil/server/api_spec/README.rst +5 -0
  45. toil/server/app.py +12 -6
  46. toil/server/cli/wes_cwl_runner.py +3 -2
  47. toil/server/wes/abstract_backend.py +21 -43
  48. toil/server/wes/toil_backend.py +2 -2
  49. toil/test/__init__.py +275 -115
  50. toil/test/batchSystems/batchSystemTest.py +228 -213
  51. toil/test/batchSystems/batch_system_plugin_test.py +7 -0
  52. toil/test/batchSystems/test_slurm.py +27 -0
  53. toil/test/cactus/pestis.tar.gz +0 -0
  54. toil/test/conftest.py +7 -0
  55. toil/test/cwl/2.fasta +11 -0
  56. toil/test/cwl/2.fastq +12 -0
  57. toil/test/cwl/conftest.py +1 -1
  58. toil/test/cwl/cwlTest.py +1175 -870
  59. toil/test/cwl/directory/directory/file.txt +15 -0
  60. toil/test/cwl/download_directory_file.json +4 -0
  61. toil/test/cwl/download_directory_s3.json +4 -0
  62. toil/test/cwl/download_file.json +6 -0
  63. toil/test/cwl/download_http.json +6 -0
  64. toil/test/cwl/download_https.json +6 -0
  65. toil/test/cwl/download_s3.json +6 -0
  66. toil/test/cwl/download_subdirectory_file.json +5 -0
  67. toil/test/cwl/download_subdirectory_s3.json +5 -0
  68. toil/test/cwl/empty.json +1 -0
  69. toil/test/cwl/mock_mpi/fake_mpi.yml +8 -0
  70. toil/test/cwl/mock_mpi/fake_mpi_run.py +42 -0
  71. toil/test/cwl/optional-file-exists.json +6 -0
  72. toil/test/cwl/optional-file-missing.json +6 -0
  73. toil/test/cwl/preemptible_expression.json +1 -0
  74. toil/test/cwl/revsort-job-missing.json +6 -0
  75. toil/test/cwl/revsort-job.json +6 -0
  76. toil/test/cwl/s3_secondary_file.json +16 -0
  77. toil/test/cwl/seqtk_seq_job.json +6 -0
  78. toil/test/cwl/stream.json +6 -0
  79. toil/test/cwl/test_filename_conflict_resolution.ms/table.dat +0 -0
  80. toil/test/cwl/test_filename_conflict_resolution.ms/table.f0 +0 -0
  81. toil/test/cwl/test_filename_conflict_resolution.ms/table.f1 +0 -0
  82. toil/test/cwl/test_filename_conflict_resolution.ms/table.f1i +0 -0
  83. toil/test/cwl/test_filename_conflict_resolution.ms/table.f2 +0 -0
  84. toil/test/cwl/test_filename_conflict_resolution.ms/table.f2_TSM0 +0 -0
  85. toil/test/cwl/test_filename_conflict_resolution.ms/table.f3 +0 -0
  86. toil/test/cwl/test_filename_conflict_resolution.ms/table.f3_TSM0 +0 -0
  87. toil/test/cwl/test_filename_conflict_resolution.ms/table.f4 +0 -0
  88. toil/test/cwl/test_filename_conflict_resolution.ms/table.f4_TSM0 +0 -0
  89. toil/test/cwl/test_filename_conflict_resolution.ms/table.f5 +0 -0
  90. toil/test/cwl/test_filename_conflict_resolution.ms/table.info +0 -0
  91. toil/test/cwl/test_filename_conflict_resolution.ms/table.lock +0 -0
  92. toil/test/cwl/whale.txt +16 -0
  93. toil/test/docs/scripts/example_alwaysfail.py +38 -0
  94. toil/test/docs/scripts/example_alwaysfail_with_files.wdl +33 -0
  95. toil/test/docs/scripts/example_cachingbenchmark.py +117 -0
  96. toil/test/docs/scripts/stagingExampleFiles/in.txt +1 -0
  97. toil/test/docs/scripts/stagingExampleFiles/out.txt +2 -0
  98. toil/test/docs/scripts/tutorial_arguments.py +23 -0
  99. toil/test/docs/scripts/tutorial_debugging.patch +12 -0
  100. toil/test/docs/scripts/tutorial_debugging_hangs.wdl +126 -0
  101. toil/test/docs/scripts/tutorial_debugging_works.wdl +129 -0
  102. toil/test/docs/scripts/tutorial_docker.py +20 -0
  103. toil/test/docs/scripts/tutorial_dynamic.py +24 -0
  104. toil/test/docs/scripts/tutorial_encapsulation.py +28 -0
  105. toil/test/docs/scripts/tutorial_encapsulation2.py +29 -0
  106. toil/test/docs/scripts/tutorial_helloworld.py +15 -0
  107. toil/test/docs/scripts/tutorial_invokeworkflow.py +27 -0
  108. toil/test/docs/scripts/tutorial_invokeworkflow2.py +30 -0
  109. toil/test/docs/scripts/tutorial_jobfunctions.py +22 -0
  110. toil/test/docs/scripts/tutorial_managing.py +29 -0
  111. toil/test/docs/scripts/tutorial_managing2.py +56 -0
  112. toil/test/docs/scripts/tutorial_multiplejobs.py +25 -0
  113. toil/test/docs/scripts/tutorial_multiplejobs2.py +21 -0
  114. toil/test/docs/scripts/tutorial_multiplejobs3.py +22 -0
  115. toil/test/docs/scripts/tutorial_promises.py +25 -0
  116. toil/test/docs/scripts/tutorial_promises2.py +30 -0
  117. toil/test/docs/scripts/tutorial_quickstart.py +22 -0
  118. toil/test/docs/scripts/tutorial_requirements.py +44 -0
  119. toil/test/docs/scripts/tutorial_services.py +45 -0
  120. toil/test/docs/scripts/tutorial_staging.py +45 -0
  121. toil/test/docs/scripts/tutorial_stats.py +64 -0
  122. toil/test/docs/scriptsTest.py +2 -1
  123. toil/test/lib/aws/test_iam.py +3 -1
  124. toil/test/lib/dockerTest.py +205 -122
  125. toil/test/lib/test_history.py +101 -77
  126. toil/test/lib/test_url.py +69 -0
  127. toil/test/lib/url_plugin_test.py +105 -0
  128. toil/test/provisioners/aws/awsProvisionerTest.py +13 -10
  129. toil/test/provisioners/clusterTest.py +17 -4
  130. toil/test/provisioners/gceProvisionerTest.py +17 -15
  131. toil/test/server/serverTest.py +78 -36
  132. toil/test/sort/sort.py +4 -1
  133. toil/test/src/busTest.py +17 -17
  134. toil/test/src/deferredFunctionTest.py +145 -132
  135. toil/test/src/importExportFileTest.py +71 -63
  136. toil/test/src/jobEncapsulationTest.py +27 -28
  137. toil/test/src/jobServiceTest.py +149 -133
  138. toil/test/src/jobTest.py +219 -211
  139. toil/test/src/miscTests.py +66 -60
  140. toil/test/src/promisedRequirementTest.py +163 -169
  141. toil/test/src/regularLogTest.py +24 -24
  142. toil/test/src/resourceTest.py +82 -76
  143. toil/test/src/restartDAGTest.py +51 -47
  144. toil/test/src/resumabilityTest.py +24 -19
  145. toil/test/src/retainTempDirTest.py +60 -57
  146. toil/test/src/systemTest.py +17 -13
  147. toil/test/src/threadingTest.py +29 -32
  148. toil/test/utils/ABCWorkflowDebug/B_file.txt +1 -0
  149. toil/test/utils/ABCWorkflowDebug/debugWorkflow.py +204 -0
  150. toil/test/utils/ABCWorkflowDebug/mkFile.py +16 -0
  151. toil/test/utils/ABCWorkflowDebug/sleep.cwl +12 -0
  152. toil/test/utils/ABCWorkflowDebug/sleep.yaml +1 -0
  153. toil/test/utils/toilDebugTest.py +117 -102
  154. toil/test/utils/toilKillTest.py +54 -53
  155. toil/test/utils/utilsTest.py +303 -229
  156. toil/test/wdl/lint_error.wdl +9 -0
  157. toil/test/wdl/md5sum/empty_file.json +1 -0
  158. toil/test/wdl/md5sum/md5sum-gs.json +1 -0
  159. toil/test/wdl/md5sum/md5sum.1.0.wdl +32 -0
  160. toil/test/wdl/md5sum/md5sum.input +1 -0
  161. toil/test/wdl/md5sum/md5sum.json +1 -0
  162. toil/test/wdl/md5sum/md5sum.wdl +25 -0
  163. toil/test/wdl/miniwdl_self_test/inputs-namespaced.json +1 -0
  164. toil/test/wdl/miniwdl_self_test/inputs.json +1 -0
  165. toil/test/wdl/miniwdl_self_test/self_test.wdl +40 -0
  166. toil/test/wdl/standard_library/as_map.json +16 -0
  167. toil/test/wdl/standard_library/as_map_as_input.wdl +23 -0
  168. toil/test/wdl/standard_library/as_pairs.json +7 -0
  169. toil/test/wdl/standard_library/as_pairs_as_input.wdl +23 -0
  170. toil/test/wdl/standard_library/ceil.json +3 -0
  171. toil/test/wdl/standard_library/ceil_as_command.wdl +16 -0
  172. toil/test/wdl/standard_library/ceil_as_input.wdl +16 -0
  173. toil/test/wdl/standard_library/collect_by_key.json +1 -0
  174. toil/test/wdl/standard_library/collect_by_key_as_input.wdl +23 -0
  175. toil/test/wdl/standard_library/cross.json +11 -0
  176. toil/test/wdl/standard_library/cross_as_input.wdl +19 -0
  177. toil/test/wdl/standard_library/flatten.json +7 -0
  178. toil/test/wdl/standard_library/flatten_as_input.wdl +18 -0
  179. toil/test/wdl/standard_library/floor.json +3 -0
  180. toil/test/wdl/standard_library/floor_as_command.wdl +16 -0
  181. toil/test/wdl/standard_library/floor_as_input.wdl +16 -0
  182. toil/test/wdl/standard_library/keys.json +8 -0
  183. toil/test/wdl/standard_library/keys_as_input.wdl +24 -0
  184. toil/test/wdl/standard_library/length.json +7 -0
  185. toil/test/wdl/standard_library/length_as_input.wdl +16 -0
  186. toil/test/wdl/standard_library/length_as_input_with_map.json +7 -0
  187. toil/test/wdl/standard_library/length_as_input_with_map.wdl +17 -0
  188. toil/test/wdl/standard_library/length_invalid.json +3 -0
  189. toil/test/wdl/standard_library/range.json +3 -0
  190. toil/test/wdl/standard_library/range_0.json +3 -0
  191. toil/test/wdl/standard_library/range_as_input.wdl +17 -0
  192. toil/test/wdl/standard_library/range_invalid.json +3 -0
  193. toil/test/wdl/standard_library/read_boolean.json +3 -0
  194. toil/test/wdl/standard_library/read_boolean_as_command.wdl +17 -0
  195. toil/test/wdl/standard_library/read_float.json +3 -0
  196. toil/test/wdl/standard_library/read_float_as_command.wdl +17 -0
  197. toil/test/wdl/standard_library/read_int.json +3 -0
  198. toil/test/wdl/standard_library/read_int_as_command.wdl +17 -0
  199. toil/test/wdl/standard_library/read_json.json +3 -0
  200. toil/test/wdl/standard_library/read_json_as_output.wdl +31 -0
  201. toil/test/wdl/standard_library/read_lines.json +3 -0
  202. toil/test/wdl/standard_library/read_lines_as_output.wdl +31 -0
  203. toil/test/wdl/standard_library/read_map.json +3 -0
  204. toil/test/wdl/standard_library/read_map_as_output.wdl +31 -0
  205. toil/test/wdl/standard_library/read_string.json +3 -0
  206. toil/test/wdl/standard_library/read_string_as_command.wdl +17 -0
  207. toil/test/wdl/standard_library/read_tsv.json +3 -0
  208. toil/test/wdl/standard_library/read_tsv_as_output.wdl +31 -0
  209. toil/test/wdl/standard_library/round.json +3 -0
  210. toil/test/wdl/standard_library/round_as_command.wdl +16 -0
  211. toil/test/wdl/standard_library/round_as_input.wdl +16 -0
  212. toil/test/wdl/standard_library/size.json +3 -0
  213. toil/test/wdl/standard_library/size_as_command.wdl +17 -0
  214. toil/test/wdl/standard_library/size_as_output.wdl +36 -0
  215. toil/test/wdl/standard_library/stderr.json +3 -0
  216. toil/test/wdl/standard_library/stderr_as_output.wdl +30 -0
  217. toil/test/wdl/standard_library/stdout.json +3 -0
  218. toil/test/wdl/standard_library/stdout_as_output.wdl +30 -0
  219. toil/test/wdl/standard_library/sub.json +3 -0
  220. toil/test/wdl/standard_library/sub_as_input.wdl +17 -0
  221. toil/test/wdl/standard_library/sub_as_input_with_file.wdl +17 -0
  222. toil/test/wdl/standard_library/transpose.json +6 -0
  223. toil/test/wdl/standard_library/transpose_as_input.wdl +18 -0
  224. toil/test/wdl/standard_library/write_json.json +6 -0
  225. toil/test/wdl/standard_library/write_json_as_command.wdl +17 -0
  226. toil/test/wdl/standard_library/write_lines.json +7 -0
  227. toil/test/wdl/standard_library/write_lines_as_command.wdl +17 -0
  228. toil/test/wdl/standard_library/write_map.json +6 -0
  229. toil/test/wdl/standard_library/write_map_as_command.wdl +17 -0
  230. toil/test/wdl/standard_library/write_tsv.json +6 -0
  231. toil/test/wdl/standard_library/write_tsv_as_command.wdl +17 -0
  232. toil/test/wdl/standard_library/zip.json +12 -0
  233. toil/test/wdl/standard_library/zip_as_input.wdl +19 -0
  234. toil/test/wdl/test.csv +3 -0
  235. toil/test/wdl/test.tsv +3 -0
  236. toil/test/wdl/testfiles/croo.wdl +38 -0
  237. toil/test/wdl/testfiles/drop_files.wdl +62 -0
  238. toil/test/wdl/testfiles/drop_files_subworkflow.wdl +13 -0
  239. toil/test/wdl/testfiles/empty.txt +0 -0
  240. toil/test/wdl/testfiles/not_enough_outputs.wdl +33 -0
  241. toil/test/wdl/testfiles/random.wdl +66 -0
  242. toil/test/wdl/testfiles/read_file.wdl +18 -0
  243. toil/test/wdl/testfiles/string_file_coercion.json +1 -0
  244. toil/test/wdl/testfiles/string_file_coercion.wdl +35 -0
  245. toil/test/wdl/testfiles/test.json +4 -0
  246. toil/test/wdl/testfiles/test_boolean.txt +1 -0
  247. toil/test/wdl/testfiles/test_float.txt +1 -0
  248. toil/test/wdl/testfiles/test_int.txt +1 -0
  249. toil/test/wdl/testfiles/test_lines.txt +5 -0
  250. toil/test/wdl/testfiles/test_map.txt +2 -0
  251. toil/test/wdl/testfiles/test_string.txt +1 -0
  252. toil/test/wdl/testfiles/url_to_file.wdl +13 -0
  253. toil/test/wdl/testfiles/url_to_optional_file.wdl +14 -0
  254. toil/test/wdl/testfiles/vocab.json +1 -0
  255. toil/test/wdl/testfiles/vocab.wdl +66 -0
  256. toil/test/wdl/testfiles/wait.wdl +34 -0
  257. toil/test/wdl/wdl_specification/type_pair.json +23 -0
  258. toil/test/wdl/wdl_specification/type_pair_basic.wdl +36 -0
  259. toil/test/wdl/wdl_specification/type_pair_with_files.wdl +36 -0
  260. toil/test/wdl/wdl_specification/v1_spec.json +1 -0
  261. toil/test/wdl/wdl_specification/v1_spec_declaration.wdl +39 -0
  262. toil/test/wdl/wdltoil_test.py +751 -529
  263. toil/test/wdl/wdltoil_test_kubernetes.py +2 -2
  264. toil/utils/toilSshCluster.py +23 -0
  265. toil/utils/toilUpdateEC2Instances.py +1 -0
  266. toil/version.py +5 -5
  267. toil/wdl/wdltoil.py +518 -437
  268. toil/worker.py +11 -6
  269. {toil-8.1.0b1.dist-info → toil-9.0.0.dist-info}/METADATA +25 -24
  270. toil-9.0.0.dist-info/RECORD +444 -0
  271. {toil-8.1.0b1.dist-info → toil-9.0.0.dist-info}/WHEEL +1 -1
  272. toil-8.1.0b1.dist-info/RECORD +0 -259
  273. {toil-8.1.0b1.dist-info → toil-9.0.0.dist-info}/entry_points.txt +0 -0
  274. {toil-8.1.0b1.dist-info → toil-9.0.0.dist-info/licenses}/LICENSE +0 -0
  275. {toil-8.1.0b1.dist-info → toil-9.0.0.dist-info}/top_level.txt +0 -0
@@ -11,72 +11,81 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
+ from argparse import Namespace
15
+ from collections.abc import Callable, Generator, Sequence
14
16
  import logging
15
17
  import os
18
+ from pathlib import Path
16
19
  import signal
17
20
  import time
18
- from abc import ABCMeta
21
+ from typing import Optional
19
22
  from uuid import uuid4
20
23
 
21
24
  import psutil
25
+ import pytest
22
26
 
23
27
  from toil.exceptions import FailedJobsException
24
28
  from toil.job import Job
25
29
  from toil.lib.threading import cpu_count
26
- from toil.test import ToilTest, slow
30
+ from toil.test import pslow as slow
27
31
 
28
32
  logger = logging.getLogger(__name__)
29
33
 
30
34
 
31
- class DeferredFunctionTest(ToilTest, metaclass=ABCMeta):
32
- """Test the deferred function system."""
35
+ @pytest.fixture(scope="function")
36
+ def options(tmp_path: Path) -> Generator[Namespace]:
37
+ try:
38
+ testDir = tmp_path / "testDir"
39
+ testDir.mkdir()
40
+ options = Job.Runner.getDefaultOptions(tmp_path / "jobstore")
41
+ options.logLevel = "INFO"
42
+ options.workDir = str(testDir)
43
+ options.clean = "always"
44
+ options.logFile = str(testDir / "logFile")
45
+ yield options
46
+ finally:
47
+ pass # no cleanup
48
+
33
49
 
34
- # This determines what job store type to use.
35
- jobStoreType = "file"
36
-
37
- def _getTestJobStore(self):
38
- if self.jobStoreType == "file":
39
- return self._getTestJobStorePath()
40
- elif self.jobStoreType == "aws":
41
- return f"aws:{self.awsRegion()}:cache-tests-{uuid4()}"
42
- elif self.jobStoreType == "google":
43
- projectID = os.getenv("TOIL_GOOGLE_PROJECTID")
44
- return f"google:{projectID}:cache-tests-{str(uuid4())}"
45
- else:
46
- raise RuntimeError("Illegal job store type.")
47
-
48
- def setUp(self):
49
- super().setUp()
50
- testDir = self._createTempDir()
51
- self.options = Job.Runner.getDefaultOptions(self._getTestJobStore())
52
- self.options.logLevel = "INFO"
53
- self.options.workDir = testDir
54
- self.options.clean = "always"
55
- self.options.logFile = os.path.join(testDir, "logFile")
50
+ class TestDeferredFunction:
51
+ """Test the deferred function system."""
56
52
 
57
53
  # Tests for the various defer possibilities
58
- def testDeferredFunctionRunsWithMethod(self):
54
+ def testDeferredFunctionRunsWithMethod(
55
+ self, tmp_path: Path, options: Namespace
56
+ ) -> None:
59
57
  """
60
58
  Refer docstring in _testDeferredFunctionRuns.
61
59
  Test with Method
62
60
  """
63
- self._testDeferredFunctionRuns(_writeNonLocalFilesMethod)
61
+ self._testDeferredFunctionRuns(tmp_path, options, _writeNonLocalFilesMethod)
64
62
 
65
- def testDeferredFunctionRunsWithClassMethod(self):
63
+ def testDeferredFunctionRunsWithClassMethod(
64
+ self, tmp_path: Path, options: Namespace
65
+ ) -> None:
66
66
  """
67
67
  Refer docstring in _testDeferredFunctionRuns.
68
68
  Test with Class Method
69
69
  """
70
- self._testDeferredFunctionRuns(_writeNonLocalFilesClassMethod)
70
+ self._testDeferredFunctionRuns(
71
+ tmp_path, options, _writeNonLocalFilesClassMethod
72
+ )
71
73
 
72
- def testDeferredFunctionRunsWithLambda(self):
74
+ def testDeferredFunctionRunsWithLambda(
75
+ self, tmp_path: Path, options: Namespace
76
+ ) -> None:
73
77
  """
74
78
  Refer docstring in _testDeferredFunctionRuns.
75
79
  Test with Lambda
76
80
  """
77
- self._testDeferredFunctionRuns(_writeNonLocalFilesLambda)
78
-
79
- def _testDeferredFunctionRuns(self, callableFn):
81
+ self._testDeferredFunctionRuns(tmp_path, options, _writeNonLocalFilesLambda)
82
+
83
+ def _testDeferredFunctionRuns(
84
+ self,
85
+ tmp_path: Path,
86
+ options: Namespace,
87
+ callableFn: Callable[[Job, tuple[Path, Path]], None],
88
+ ) -> None:
80
89
  """
81
90
  Create 2 files. Make a job that writes data to them. Register a deferred function that
82
91
  deletes the two files (one passed as an arg, and one as a kwarg) and later assert that
@@ -85,20 +94,24 @@ class DeferredFunctionTest(ToilTest, metaclass=ABCMeta):
85
94
  :param function callableFn: The function to use in the test.
86
95
  :return: None
87
96
  """
88
- workdir = self._createTempDir(purpose="nonLocalDir")
89
- nonLocalFile1 = os.path.join(workdir, str(uuid4()))
90
- nonLocalFile2 = os.path.join(workdir, str(uuid4()))
91
- open(nonLocalFile1, "w").close()
92
- open(nonLocalFile2, "w").close()
93
- assert os.path.exists(nonLocalFile1)
94
- assert os.path.exists(nonLocalFile2)
97
+ workdir = tmp_path / "nonLocalDir"
98
+ workdir.mkdir()
99
+ nonLocalFile1 = workdir / str(uuid4())
100
+ nonLocalFile2 = workdir / str(uuid4())
101
+ nonLocalFile1.touch()
102
+ nonLocalFile2.touch()
103
+ assert nonLocalFile1.exists()
104
+ assert nonLocalFile2.exists()
95
105
  A = Job.wrapJobFn(callableFn, files=(nonLocalFile1, nonLocalFile2))
96
- Job.Runner.startToil(A, self.options)
97
- assert not os.path.exists(nonLocalFile1)
98
- assert not os.path.exists(nonLocalFile2)
106
+ Job.Runner.startToil(A, options)
107
+ assert not nonLocalFile1.exists()
108
+ assert not nonLocalFile2.exists()
99
109
 
100
110
  @slow
101
- def testDeferredFunctionRunsWithFailures(self):
111
+ @pytest.mark.slow
112
+ def testDeferredFunctionRunsWithFailures(
113
+ self, options: Namespace, tmp_path: Path
114
+ ) -> None:
102
115
  """
103
116
  Create 2 non local filesto use as flags. Create a job that registers a function that
104
117
  deletes one non local file. If that file exists, the job SIGKILLs itself. If it doesn't
@@ -114,23 +127,30 @@ class DeferredFunctionTest(ToilTest, metaclass=ABCMeta):
114
127
  Incidentally, this also tests for multiple registered deferred functions, and the case
115
128
  where a deferred function fails (since the first file doesn't exist on the retry).
116
129
  """
117
- self.options.retryCount = 1
118
- workdir = self._createTempDir(purpose="nonLocalDir")
119
- nonLocalFile1 = os.path.join(workdir, str(uuid4()))
120
- nonLocalFile2 = os.path.join(workdir, str(uuid4()))
121
- open(nonLocalFile1, "w").close()
122
- open(nonLocalFile2, "w").close()
123
- assert os.path.exists(nonLocalFile1)
124
- assert os.path.exists(nonLocalFile2)
130
+ options.retryCount = 1
131
+ workdir = tmp_path / "nonLocalDir"
132
+ workdir.mkdir()
133
+ nonLocalFile1 = workdir / str(uuid4())
134
+ nonLocalFile2 = workdir / str(uuid4())
135
+ nonLocalFile1.touch()
136
+ nonLocalFile2.touch()
137
+ assert nonLocalFile1.exists()
138
+ assert nonLocalFile2.exists()
125
139
  A = Job.wrapJobFn(
126
140
  _deferredFunctionRunsWithFailuresFn, files=(nonLocalFile1, nonLocalFile2)
127
141
  )
128
- Job.Runner.startToil(A, self.options)
129
- assert not os.path.exists(nonLocalFile1)
130
- assert not os.path.exists(nonLocalFile2)
142
+ Job.Runner.startToil(A, options)
143
+ assert not nonLocalFile1.exists()
144
+ assert not nonLocalFile2.exists()
131
145
 
132
146
  @slow
133
- def testNewJobsCanHandleOtherJobDeaths(self):
147
+ @pytest.mark.slow
148
+ @pytest.mark.skipif(
149
+ cpu_count() < 2, reason="Not enough CPUs to run two tasks at once"
150
+ )
151
+ def testNewJobsCanHandleOtherJobDeaths(
152
+ self, options: Namespace, tmp_path: Path
153
+ ) -> None:
134
154
  """
135
155
  Create 2 non-local files and then create 2 jobs. The first job registers a deferred job
136
156
  to delete the second non-local file, deletes the first non-local file and then kills
@@ -141,19 +161,16 @@ class DeferredFunctionTest(ToilTest, metaclass=ABCMeta):
141
161
  end of the run.
142
162
  """
143
163
 
144
- # Check to make sure we can run two jobs in parallel
145
- cpus = cpu_count()
146
- assert cpus >= 2, "Not enough CPUs to run two tasks at once"
147
-
148
164
  # There can be no retries
149
- self.options.retryCount = 0
150
- workdir = self._createTempDir(purpose="nonLocalDir")
151
- nonLocalFile1 = os.path.join(workdir, str(uuid4()))
152
- nonLocalFile2 = os.path.join(workdir, str(uuid4()))
153
- open(nonLocalFile1, "w").close()
154
- open(nonLocalFile2, "w").close()
155
- assert os.path.exists(nonLocalFile1)
156
- assert os.path.exists(nonLocalFile2)
165
+ options.retryCount = 0
166
+ workdir = tmp_path / "nonLocalDir"
167
+ workdir.mkdir()
168
+ nonLocalFile1 = workdir / str(uuid4())
169
+ nonLocalFile2 = workdir / str(uuid4())
170
+ nonLocalFile1.touch()
171
+ nonLocalFile2.touch()
172
+ assert nonLocalFile1.exists()
173
+ assert nonLocalFile2.exists()
157
174
  files = [nonLocalFile1, nonLocalFile2]
158
175
  root = Job()
159
176
  # A and B here must run in parallel for this to work
@@ -169,11 +186,13 @@ class DeferredFunctionTest(ToilTest, metaclass=ABCMeta):
169
186
  root.addChild(B)
170
187
  B.addChild(C)
171
188
  try:
172
- Job.Runner.startToil(root, self.options)
173
- except FailedJobsException as e:
189
+ Job.Runner.startToil(root, options)
190
+ except FailedJobsException:
174
191
  pass
175
192
 
176
- def testBatchSystemCleanupCanHandleWorkerDeaths(self):
193
+ def testBatchSystemCleanupCanHandleWorkerDeaths(
194
+ self, options: Namespace, tmp_path: Path
195
+ ) -> None:
177
196
  """
178
197
  Create some non-local files. Create a job that registers a deferred
179
198
  function to delete the file and then kills its worker.
@@ -184,17 +203,17 @@ class DeferredFunctionTest(ToilTest, metaclass=ABCMeta):
184
203
  """
185
204
 
186
205
  # There can be no retries
187
- self.options.retryCount = 0
188
- workdir = self._createTempDir(purpose="nonLocalDir")
189
- nonLocalFile1 = os.path.join(workdir, str(uuid4()))
190
- nonLocalFile2 = os.path.join(workdir, str(uuid4()))
206
+ options.retryCount = 0
207
+ workdir = tmp_path / "nonLocalDir"
208
+ workdir.mkdir()
209
+ nonLocalFile1 = workdir / str(uuid4())
210
+ nonLocalFile2 = workdir / str(uuid4())
191
211
  # The first file has to be non zero or meseeks will go into an infinite sleep
192
- file1 = open(nonLocalFile1, "w")
193
- file1.write("test")
194
- file1.close()
195
- open(nonLocalFile2, "w").close()
196
- assert os.path.exists(nonLocalFile1)
197
- assert os.path.exists(nonLocalFile2)
212
+ with nonLocalFile1.open("w") as file1:
213
+ file1.write("test")
214
+ nonLocalFile2.touch()
215
+ assert nonLocalFile1.exists()
216
+ assert nonLocalFile2.exists()
198
217
  # We only use the "A" job here, and we fill in the first file, so all
199
218
  # it will do is defer deleting the second file, delete the first file,
200
219
  # and die.
@@ -202,117 +221,111 @@ class DeferredFunctionTest(ToilTest, metaclass=ABCMeta):
202
221
  _testNewJobsCanHandleOtherJobDeaths_A, files=(nonLocalFile1, nonLocalFile2)
203
222
  )
204
223
  try:
205
- Job.Runner.startToil(A, self.options)
224
+ Job.Runner.startToil(A, options)
206
225
  except FailedJobsException:
207
226
  pass
208
- assert not os.path.exists(nonLocalFile1)
209
- assert not os.path.exists(nonLocalFile2)
227
+ assert not nonLocalFile1.exists()
228
+ assert not nonLocalFile2.exists()
210
229
 
211
230
 
212
- def _writeNonLocalFilesMethod(job, files):
231
+ def _writeNonLocalFilesMethod(job: Job, files: tuple[Path, Path]) -> None:
213
232
  """
214
233
  Write some data to 2 files. Pass them to a registered deferred method.
215
234
 
216
235
  :param tuple files: the tuple of the two files to work with
217
- :return: None
218
236
  """
219
237
  for nlf in files:
220
- with open(nlf, "wb") as nonLocalFileHandle:
238
+ with nlf.open("wb") as nonLocalFileHandle:
221
239
  nonLocalFileHandle.write(os.urandom(1 * 1024 * 1024))
222
240
  job.defer(_deleteMethods._deleteFileMethod, files[0], nlf=files[1])
223
- return None
224
241
 
225
242
 
226
- def _writeNonLocalFilesClassMethod(job, files):
243
+ def _writeNonLocalFilesClassMethod(job: Job, files: tuple[Path, Path]) -> None:
227
244
  """
228
245
  Write some data to 2 files. Pass them to a registered deferred class method.
229
246
 
230
247
  :param tuple files: the tuple of the two files to work with
231
- :return: None
232
248
  """
233
249
  for nlf in files:
234
- with open(nlf, "wb") as nonLocalFileHandle:
250
+ with nlf.open("wb") as nonLocalFileHandle:
235
251
  nonLocalFileHandle.write(os.urandom(1 * 1024 * 1024))
236
252
  job.defer(_deleteMethods._deleteFileClassMethod, files[0], nlf=files[1])
237
- return None
238
253
 
239
254
 
240
- def _writeNonLocalFilesLambda(job, files):
255
+ def _writeNonLocalFilesLambda(job: Job, files: tuple[Path, Path]) -> None:
241
256
  """
242
257
  Write some data to 2 files. Pass them to a registered deferred Lambda.
243
258
 
244
259
  :param tuple files: the tuple of the two files to work with
245
- :return: None
246
260
  """
247
- lmd = lambda x, nlf: [os.remove(x), os.remove(nlf)]
261
+
262
+ def lmd(x: Path, nlf: Path) -> None:
263
+ x.unlink()
264
+ nlf.unlink()
248
265
  for nlf in files:
249
- with open(nlf, "wb") as nonLocalFileHandle:
266
+ with nlf.open("wb") as nonLocalFileHandle:
250
267
  nonLocalFileHandle.write(os.urandom(1 * 1024 * 1024))
251
268
  job.defer(lmd, files[0], nlf=files[1])
252
- return None
253
269
 
254
270
 
255
- def _deferredFunctionRunsWithFailuresFn(job, files):
271
+ def _deferredFunctionRunsWithFailuresFn(job: Job, files: tuple[Path, Path]) -> None:
256
272
  """
257
273
  Refer testDeferredFunctionRunsWithFailures
258
274
 
259
275
  :param tuple files: the tuple of the two files to work with
260
- :return: None
261
276
  """
262
277
  job.defer(_deleteFile, files[0])
263
- if os.path.exists(files[0]):
278
+ if files[0].exists():
264
279
  os.kill(os.getpid(), signal.SIGKILL)
265
280
  else:
266
- assert os.path.exists(files[1])
281
+ assert files[1].exists()
267
282
  job.defer(_deleteFile, files[1])
268
283
 
269
284
 
270
- def _deleteFile(nonLocalFile, nlf=None):
285
+ def _deleteFile(nonLocalFile: Path, nlf: Optional[Path] = None) -> None:
271
286
  """
272
287
  Delete nonLocalFile and nlf
273
- :param str nonLocalFile:
274
- :param str nlf:
275
- :return: None
288
+ :param nonLocalFile:
289
+ :param nlf:
276
290
  """
277
291
  logger.debug("Removing file: %s", nonLocalFile)
278
- os.remove(nonLocalFile)
292
+ nonLocalFile.unlink()
279
293
  logger.debug("Successfully removed file: %s", nonLocalFile)
280
294
  if nlf is not None:
281
295
  logger.debug("Removing file: %s", nlf)
282
- os.remove(nlf)
296
+ nlf.unlink()
283
297
  logger.debug("Successfully removed file: %s", nlf)
284
298
 
285
299
 
286
- def _testNewJobsCanHandleOtherJobDeaths_A(job, files):
300
+ def _testNewJobsCanHandleOtherJobDeaths_A(job: Job, files: tuple[Path, Path]) -> None:
287
301
  """
288
302
  Defer deletion of files[1], then wait for _testNewJobsCanHandleOtherJobDeaths_B to
289
303
  start up, and finally delete files[0] before sigkilling self.
290
304
 
291
305
  :param tuple files: the tuple of the two files to work with
292
- :return: None
293
306
  """
294
307
 
295
308
  # Write the pid to files[1] such that we can be sure that this process has died before
296
309
  # we spawn the next job that will do the cleanup.
297
- with open(files[1], "w") as fileHandle:
310
+ with files[1].open("w") as fileHandle:
298
311
  fileHandle.write(str(os.getpid()))
299
312
  job.defer(_deleteFile, files[1])
300
313
  logger.info("Deferred delete of %s", files[1])
301
- while os.stat(files[0]).st_size == 0:
314
+ while files[0].stat().st_size == 0:
302
315
  time.sleep(0.5)
303
- os.remove(files[0])
316
+ files[0].unlink()
304
317
  os.kill(os.getpid(), signal.SIGKILL)
305
318
 
306
319
 
307
- def _testNewJobsCanHandleOtherJobDeaths_B(job, files):
320
+ def _testNewJobsCanHandleOtherJobDeaths_B(job: Job, files: tuple[Path, Path]) -> None:
308
321
  # Write something to files[0] such that we can be sure that this process has started
309
322
  # before _testNewJobsCanHandleOtherJobDeaths_A kills itself.
310
- with open(files[0], "w") as fileHandle:
323
+ with files[0].open("w") as fileHandle:
311
324
  fileHandle.write(str(os.getpid()))
312
- while os.path.exists(files[0]):
325
+ while files[0].exists():
313
326
  time.sleep(0.5)
314
327
  # Get the pid of _testNewJobsCanHandleOtherJobDeaths_A and wait for it to truly be dead.
315
- with open(files[1]) as fileHandle:
328
+ with files[1].open() as fileHandle:
316
329
  pid = int(fileHandle.read())
317
330
  assert pid > 0
318
331
  while psutil.pid_exists(pid):
@@ -321,37 +334,37 @@ def _testNewJobsCanHandleOtherJobDeaths_B(job, files):
321
334
  # spawn the next job
322
335
 
323
336
 
324
- def _testNewJobsCanHandleOtherJobDeaths_C(job, files, expectedResult):
337
+ def _testNewJobsCanHandleOtherJobDeaths_C(
338
+ job: Job, files: Sequence[Path], expectedResult: bool
339
+ ) -> None:
325
340
  """
326
341
  Asserts whether the files exist or not.
327
342
 
328
- :param Job job: Job
329
- :param list files: list of files to test
330
- :param bool expectedResult: Are we expecting the files to exist or not?
343
+ :param job: Job
344
+ :param files: list of files to test
345
+ :param expectedResult: Are we expecting the files to exist or not?
331
346
  """
332
347
  for testFile in files:
333
- assert os.path.exists(testFile) is expectedResult
348
+ assert testFile.exists() is expectedResult
334
349
 
335
350
 
336
351
  class _deleteMethods:
337
352
  @staticmethod
338
- def _deleteFileMethod(nonLocalFile, nlf=None):
353
+ def _deleteFileMethod(nonLocalFile: Path, nlf: Optional[Path] = None) -> None:
339
354
  """
340
355
  Delete nonLocalFile and nlf
341
-
342
- :return: None
343
356
  """
344
- os.remove(nonLocalFile)
357
+ nonLocalFile.unlink()
345
358
  if nlf is not None:
346
- os.remove(nlf)
359
+ nlf.unlink()
347
360
 
348
361
  @classmethod
349
- def _deleteFileClassMethod(cls, nonLocalFile, nlf=None):
362
+ def _deleteFileClassMethod(
363
+ cls, nonLocalFile: Path, nlf: Optional[Path] = None
364
+ ) -> None:
350
365
  """
351
366
  Delete nonLocalFile and nlf
352
-
353
- :return: None
354
367
  """
355
- os.remove(nonLocalFile)
368
+ nonLocalFile.unlink()
356
369
  if nlf is not None:
357
- os.remove(nlf)
370
+ nlf.unlink()