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
@@ -12,44 +12,42 @@
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 collections.abc import Generator
16
+ from pathlib import Path
16
17
  import logging
17
- import pytest
18
18
  import time
19
- from toil.test import ToilTest
20
19
 
21
20
  from toil.lib.history import HistoryManager
22
21
 
22
+ import pytest
23
+
23
24
  logger = logging.getLogger(__name__)
24
25
  logging.basicConfig(level=logging.DEBUG)
25
26
 
26
- class HistoryTest(ToilTest):
27
+
28
+ class TestHistory:
27
29
  """
28
30
  Tests for Toil history tracking.
29
31
 
30
32
  Each test gets its own history database.
31
33
  """
32
34
 
33
- def setUp(self) -> None:
34
- super().setUp()
35
-
36
- # Apply a temp dir override to history tracking
37
- temp_dir = self._createTempDir()
38
- HistoryManager.database_path_override = os.path.join(temp_dir, "test-db.sqlite")
39
-
40
- # Flag on job history tracking
41
- self.original_flag = HistoryManager.JOB_HISTORY_ENABLED
42
- HistoryManager.JOB_HISTORY_ENABLED = True
43
-
44
-
45
- def tearDown(self) -> None:
46
- # Remove the temp dir override from history tracking
47
- HistoryManager.database_path_override = None
48
-
49
- # Restore job history tracking flag
50
- HistoryManager.JOB_HISTORY_ENABLED = self.original_flag
51
-
52
- super().tearDown()
35
+ @pytest.fixture(autouse=True, scope="function")
36
+ def private_history_manager(
37
+ self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
38
+ ) -> Generator[None]:
39
+ try:
40
+ with monkeypatch.context() as m:
41
+ m.setattr(
42
+ HistoryManager,
43
+ "database_path_override",
44
+ str(tmp_path / "test-db.sqlite"),
45
+ )
46
+ m.setattr(HistoryManager, "enabled", lambda: True)
47
+ m.setattr(HistoryManager, "enabled_job", lambda: True)
48
+ yield
49
+ finally:
50
+ pass # no cleanup needed
53
51
 
54
52
  def make_fake_workflow(self, workflow_id: str) -> None:
55
53
  # Make a fake workflow
@@ -57,15 +55,17 @@ class HistoryTest(ToilTest):
57
55
  HistoryManager.record_workflow_creation(workflow_id, workflow_jobstore_spec)
58
56
  workflow_name = "SuperCoolWF"
59
57
  workflow_trs_spec = "#wf:v1"
60
- HistoryManager.record_workflow_metadata(workflow_id, workflow_name, workflow_trs_spec)
61
-
58
+ HistoryManager.record_workflow_metadata(
59
+ workflow_id, workflow_name, workflow_trs_spec
60
+ )
61
+
62
62
  # Give it a job
63
63
  workflow_attempt_number = 1
64
64
  job_name = "DoThing"
65
65
  succeeded = True
66
66
  start_time = time.time()
67
67
  runtime = 0.1
68
- HistoryManager.record_job_attempt(
68
+ HistoryManager.record_job_attempt(
69
69
  workflow_id,
70
70
  workflow_attempt_number,
71
71
  job_name,
@@ -93,92 +93,126 @@ class HistoryTest(ToilTest):
93
93
  workflow_attempt_number = 1
94
94
 
95
95
  # Make sure we have data
96
- self.assertEqual(HistoryManager.count_workflows(), 1)
97
- self.assertEqual(HistoryManager.count_workflow_attempts(), 1)
98
- self.assertEqual(HistoryManager.count_job_attempts(), 1)
96
+ assert HistoryManager.count_workflows() == 1
97
+ assert HistoryManager.count_workflow_attempts() == 1
98
+ assert HistoryManager.count_job_attempts() == 1
99
99
 
100
100
  # Make sure we see it as submittable
101
- submittable_workflow_attempts = HistoryManager.get_submittable_workflow_attempts()
102
- self.assertEqual(len(submittable_workflow_attempts), 1)
101
+ submittable_workflow_attempts = (
102
+ HistoryManager.get_submittable_workflow_attempts()
103
+ )
104
+ assert len(submittable_workflow_attempts) == 1
103
105
 
104
106
  # Make sure we see its jobs as submittable
105
- with_submittable_job_attempts = HistoryManager.get_workflow_attempts_with_submittable_job_attempts()
106
- self.assertEqual(len(with_submittable_job_attempts), 1)
107
+ with_submittable_job_attempts = (
108
+ HistoryManager.get_workflow_attempts_with_submittable_job_attempts()
109
+ )
110
+ assert len(with_submittable_job_attempts) == 1
107
111
 
108
112
  # Make sure we actually see the job
109
- submittable_job_attempts = HistoryManager.get_unsubmitted_job_attempts(workflow_id, workflow_attempt_number)
110
- self.assertEqual(len(submittable_job_attempts), 1)
113
+ submittable_job_attempts = HistoryManager.get_unsubmitted_job_attempts(
114
+ workflow_id, workflow_attempt_number
115
+ )
116
+ assert len(submittable_job_attempts) == 1
111
117
 
112
118
  # Pretend we submitted them.
113
- HistoryManager.mark_job_attempts_submitted([j.id for j in submittable_job_attempts])
114
- HistoryManager.mark_workflow_attempt_submitted(workflow_id, workflow_attempt_number)
119
+ HistoryManager.mark_job_attempts_submitted(
120
+ [j.id for j in submittable_job_attempts]
121
+ )
122
+ HistoryManager.mark_workflow_attempt_submitted(
123
+ workflow_id, workflow_attempt_number
124
+ )
115
125
 
116
126
  # Make sure they are no longer matching
117
- self.assertEqual(len(HistoryManager.get_submittable_workflow_attempts()), 0)
118
- self.assertEqual(len(HistoryManager.get_workflow_attempts_with_submittable_job_attempts()), 0)
119
- self.assertEqual(len(HistoryManager.get_unsubmitted_job_attempts(workflow_id, workflow_attempt_number)), 0)
127
+ assert len(HistoryManager.get_submittable_workflow_attempts()) == 0
128
+ assert (
129
+ len(HistoryManager.get_workflow_attempts_with_submittable_job_attempts())
130
+ == 0
131
+ )
132
+ assert (
133
+ len(
134
+ HistoryManager.get_unsubmitted_job_attempts(
135
+ workflow_id, workflow_attempt_number
136
+ )
137
+ )
138
+ == 0
139
+ )
120
140
 
121
141
  # Make sure we still have data
122
- self.assertEqual(HistoryManager.count_workflows(), 1)
123
- self.assertEqual(HistoryManager.count_workflow_attempts(), 1)
124
- self.assertEqual(HistoryManager.count_job_attempts(), 1)
142
+ assert HistoryManager.count_workflows() == 1
143
+ assert HistoryManager.count_workflow_attempts() == 1
144
+ assert HistoryManager.count_job_attempts() == 1
125
145
 
126
146
  def test_history_deletion(self) -> None:
127
147
  workflow_id = "123"
128
148
  self.make_fake_workflow(workflow_id)
129
149
  workflow_attempt_number = 1
130
-
150
+
131
151
  # Make sure we can see the workflow for deletion by age but not by done-ness
132
- self.assertEqual(len(HistoryManager.get_oldest_workflow_ids()), 1)
133
- self.assertEqual(len(HistoryManager.get_fully_submitted_workflow_ids()), 0)
152
+ assert len(HistoryManager.get_oldest_workflow_ids()) == 1
153
+ assert len(HistoryManager.get_fully_submitted_workflow_ids()) == 0
134
154
 
135
155
  # Pretend we submitted the workflow.
136
- HistoryManager.mark_job_attempts_submitted([j.id for j in HistoryManager.get_unsubmitted_job_attempts(workflow_id, workflow_attempt_number)])
137
- HistoryManager.mark_workflow_attempt_submitted(workflow_id, workflow_attempt_number)
156
+ HistoryManager.mark_job_attempts_submitted(
157
+ [
158
+ j.id
159
+ for j in HistoryManager.get_unsubmitted_job_attempts(
160
+ workflow_id, workflow_attempt_number
161
+ )
162
+ ]
163
+ )
164
+ HistoryManager.mark_workflow_attempt_submitted(
165
+ workflow_id, workflow_attempt_number
166
+ )
138
167
 
139
168
  # Make sure we can see the workflow for deletion by done-ness
140
- self.assertEqual(len(HistoryManager.get_fully_submitted_workflow_ids()), 1)
169
+ assert len(HistoryManager.get_fully_submitted_workflow_ids()) == 1
141
170
 
142
171
  # Add a new workflow
143
172
  other_workflow_id = "456"
144
173
  self.make_fake_workflow(other_workflow_id)
145
174
 
146
175
  # Make sure we can see the both for deletion by age but only one by done-ness
147
- self.assertEqual(len(HistoryManager.get_oldest_workflow_ids()), 2)
148
- self.assertEqual(len(HistoryManager.get_fully_submitted_workflow_ids()), 1)
176
+ assert len(HistoryManager.get_oldest_workflow_ids()) == 2
177
+ assert len(HistoryManager.get_fully_submitted_workflow_ids()) == 1
149
178
 
150
179
  # Make sure the older workflow is first.
151
- self.assertEqual(HistoryManager.get_oldest_workflow_ids(), [workflow_id, other_workflow_id])
180
+ assert HistoryManager.get_oldest_workflow_ids() == [
181
+ workflow_id,
182
+ other_workflow_id,
183
+ ]
152
184
 
153
185
  # Delete the new workflow
154
186
  HistoryManager.delete_workflow(other_workflow_id)
155
187
 
156
188
  # Make sure we can see the old one
157
- self.assertEqual(HistoryManager.get_oldest_workflow_ids(), [workflow_id])
158
- self.assertEqual(HistoryManager.get_fully_submitted_workflow_ids(), [workflow_id])
189
+ assert HistoryManager.get_oldest_workflow_ids() == [workflow_id]
190
+ assert HistoryManager.get_fully_submitted_workflow_ids() == [workflow_id]
159
191
 
160
192
  # Delete the old workflow
161
193
  HistoryManager.delete_workflow(workflow_id)
162
194
 
163
195
  # Make sure we have no data
164
- self.assertEqual(HistoryManager.count_workflows(), 0)
165
- self.assertEqual(HistoryManager.count_workflow_attempts(), 0)
166
- self.assertEqual(HistoryManager.count_job_attempts(), 0)
167
-
196
+ assert HistoryManager.count_workflows() == 0
197
+ assert HistoryManager.count_workflow_attempts() == 0
198
+ assert HistoryManager.count_job_attempts() == 0
168
199
 
169
200
  def test_history_size_limit(self) -> None:
170
201
  """
171
202
  Make sure the database size can be controlled.
172
203
  """
173
204
 
174
- for workflow_id in ("WorkflowThatTakesUpSomeSpace,ActuallyMoreThanTheLaterOnesTake" + str(i) for i in range(10)):
205
+ for workflow_id in (
206
+ "WorkflowThatTakesUpSomeSpace,ActuallyMoreThanTheLaterOnesTake" + str(i)
207
+ for i in range(10)
208
+ ):
175
209
  self.make_fake_workflow(workflow_id)
176
210
 
177
211
  # We should see the workflows.
178
- self.assertEqual(HistoryManager.count_workflows(), 10)
212
+ assert HistoryManager.count_workflows() == 10
179
213
  # And they take up space.
180
214
  small_size = HistoryManager.get_database_byte_size()
181
- self.assertGreater(small_size, 0)
215
+ assert small_size > 0
182
216
 
183
217
  # Add a bunch more
184
218
  for workflow_id in ("WorkflowThatTakesUpSpace" + str(i) for i in range(50)):
@@ -187,26 +221,16 @@ class HistoryTest(ToilTest):
187
221
  # We should see that this is now a much larger database
188
222
  large_size = HistoryManager.get_database_byte_size()
189
223
  logger.info("Increased database size from %s to %s", small_size, large_size)
190
- self.assertGreater(large_size, small_size)
191
-
224
+ large_size > small_size
225
+
192
226
  # We should be able to shrink it back down
193
227
  HistoryManager.enforce_byte_size_limit(small_size)
194
-
228
+
195
229
  reduced_size = HistoryManager.get_database_byte_size()
196
230
  logger.info("Decreased database size from %s to %s", large_size, reduced_size)
197
231
  # The database should be small enough
198
- self.assertLessEqual(reduced_size, small_size)
232
+ reduced_size <= small_size
199
233
  # There should still be some workflow attempts left in the smaller database (though probably not the first ones)
200
234
  remaining_workflows = HistoryManager.count_workflows()
201
235
  logger.info("Still have %s workflows", remaining_workflows)
202
- self.assertGreater(remaining_workflows, 0)
203
-
204
-
205
-
206
-
207
-
208
-
209
-
210
-
211
-
212
-
236
+ assert remaining_workflows > 0
@@ -0,0 +1,69 @@
1
+ # Copyright (C) 2015-2022 Regents of the University of California
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ import getpass
15
+ import logging
16
+
17
+ from pytest_httpserver import HTTPServer
18
+
19
+ from toil.lib.misc import get_user_name
20
+ from toil.lib.url import URLAccess
21
+ from toil.test import needs_aws_s3, needs_online
22
+
23
+
24
+ logger = logging.getLogger(__name__)
25
+ logging.basicConfig(level=logging.DEBUG)
26
+
27
+ class TestURLAccess():
28
+ """
29
+ Test URLAccess class handling read, list,
30
+ and checking the size/existence of resources at given URL
31
+ """
32
+
33
+ def test_get_url_access(self, httpserver: HTTPServer) -> None:
34
+ httpserver.expect_request("/some_url").respond_with_data("Yep that's a URL")
35
+ file_url = httpserver.url_for("/some_url")
36
+ assert URLAccess.url_exists(file_url)
37
+
38
+ @needs_aws_s3
39
+ def test_get_size(self) -> None:
40
+ size = URLAccess.get_size("s3://toil-datasets/hello.txt")
41
+ assert isinstance(size, int)
42
+ assert size > 0
43
+
44
+ @needs_aws_s3
45
+ def test_get_is_directory(self) -> None:
46
+ assert not URLAccess.get_is_directory("s3://toil-datasets/hello.txt")
47
+
48
+ @needs_aws_s3
49
+ def test_list_url(self) -> None:
50
+ test_dir = URLAccess.list_url("s3://1000genomes/")
51
+ assert isinstance(test_dir, list)
52
+ assert len(test_dir) > 0
53
+
54
+ @needs_aws_s3
55
+ def test_read_from_url(self) -> None:
56
+ import io
57
+ output = io.BytesIO()
58
+ size, executable = URLAccess.read_from_url("s3://toil-datasets/hello.txt", output)
59
+ assert isinstance(size, int)
60
+ assert size > 0
61
+ assert not executable
62
+ assert len(output.getvalue()) > 0
63
+
64
+ @needs_aws_s3
65
+ def test_open_url(self) -> None:
66
+ with URLAccess.open_url("s3://toil-datasets/hello.txt") as readable:
67
+ content = readable.read()
68
+ assert isinstance(content, bytes)
69
+ assert len(content) > 0
@@ -0,0 +1,105 @@
1
+ # Copyright (C) 2015-2025 Regents of the University of California
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ import logging
15
+ from typing import IO, Optional, Union
16
+
17
+ from configargparse import ArgParser, ArgumentParser
18
+
19
+ from toil.batchSystems.abstractBatchSystem import (
20
+ AbstractBatchSystem,
21
+ UpdatedBatchJobInfo,
22
+ )
23
+ from toil.batchSystems.cleanup_support import BatchSystemCleanupSupport
24
+ from toil.batchSystems.options import OptionSetter
25
+ from toil.batchSystems.registry import add_batch_system_factory
26
+ from toil.common import Toil, addOptions
27
+ from toil.job import JobDescription
28
+
29
+ import io
30
+ from urllib.parse import ParseResult
31
+ from toil.test import ToilTest
32
+ from toil.lib.url import URLAccess
33
+ from toil.lib.plugins import register_plugin, remove_plugin
34
+
35
+ logger = logging.getLogger(__name__)
36
+
37
+ class FakeURLPlugin(URLAccess):
38
+ @classmethod
39
+ def _supports_url(cls, url: ParseResult, export: bool = False) -> bool:
40
+ return url.scheme == "fake"
41
+
42
+ @classmethod
43
+ def _url_exists(cls, url: ParseResult) -> bool:
44
+ return url.netloc == "exists"
45
+
46
+ @classmethod
47
+ def _get_size(cls, url: ParseResult) -> int:
48
+ return 1234
49
+
50
+ @classmethod
51
+ def _get_is_directory(cls, url: ParseResult) -> bool:
52
+ return url.path.endswith("/")
53
+
54
+ @classmethod
55
+ def _list_url(cls, url: ParseResult) -> list[str]:
56
+ return ["file1.txt", "subdir/"]
57
+
58
+ @classmethod
59
+ def _read_from_url(cls, url: ParseResult, writable: IO[bytes]) -> tuple[int, bool]:
60
+ content = b"hello world"
61
+ writable.write(content)
62
+ return len(content), False
63
+
64
+ @classmethod
65
+ def _open_url(cls, url: ParseResult) -> IO[bytes]:
66
+ return io.BytesIO(b"hello world")
67
+
68
+ @classmethod
69
+ def _write_to_url(cls, readable: Union[IO[bytes], IO[str]], url: ParseResult, executable: bool = False) -> None:
70
+ pass
71
+
72
+
73
+ class TestURLAccess(ToilTest):
74
+ def setUp(self) -> None:
75
+ super().setUp()
76
+ register_plugin("url_access", "fake", lambda: FakeURLPlugin)
77
+
78
+ def tearDown(self) -> None:
79
+ remove_plugin("url_access", "fake")
80
+ super().tearDown()
81
+
82
+ def test_url_exists(self) -> None:
83
+ assert URLAccess.url_exists("fake://exists/resource") == True
84
+ assert URLAccess.url_exists("fake://missing/resource") == False
85
+
86
+ def test_get_size(self) -> None:
87
+ assert URLAccess.get_size("fake://any/resource") == 1234
88
+
89
+ def test_get_is_directory(self) -> None:
90
+ assert URLAccess.get_is_directory("fake://any/folder/") == True
91
+ assert URLAccess.get_is_directory("fake://any/file.txt") == False
92
+
93
+ def test_list_url(self) -> None:
94
+ assert URLAccess.list_url("fake://any/folder/") == ["file1.txt", "subdir/"]
95
+
96
+ def test_read_from_url(self) -> None:
97
+ output = io.BytesIO()
98
+ size, _ = URLAccess.read_from_url("fake://any/resource", output)
99
+ assert output.getvalue() == b"hello world"
100
+ assert size == len("hello world")
101
+
102
+ def test_open_url(self) -> None:
103
+ with URLAccess.open_url("fake://any/resource") as stream:
104
+ content = stream.read()
105
+ assert content == b"hello world"
@@ -28,6 +28,7 @@ from toil.provisioners import cluster_factory
28
28
  from toil.provisioners.aws.awsProvisioner import AWSProvisioner
29
29
  from toil.test import (
30
30
  ToilTest,
31
+ get_data,
31
32
  integrative,
32
33
  needs_aws_ec2,
33
34
  needs_fetchable_appliance,
@@ -135,7 +136,7 @@ class AbstractAWSAutoscaleTest(AbstractClusterTest):
135
136
  """
136
137
  return os.path.join(self.dataDir, filename)
137
138
 
138
- def rsyncUtil(self, src, dest):
139
+ def rsyncUtil(self, src: str, dest: str) -> None:
139
140
  subprocess.check_call(
140
141
  [
141
142
  "toil",
@@ -285,10 +286,11 @@ class AWSAutoscaleTest(AbstractAWSAutoscaleTest):
285
286
  with open(fileToSort, "w") as f:
286
287
  # Fixme: making this file larger causes the test to hang
287
288
  f.write("01234567890123456789012345678901")
288
- self.rsyncUtil(
289
- os.path.join(self._projectRootPath(), "src/toil/test/sort/sort.py"),
290
- ":" + self.script(),
291
- )
289
+ with get_data("test/sort/sort.py") as sort_py:
290
+ self.rsyncUtil(
291
+ sort_py,
292
+ ":" + self.script(),
293
+ )
292
294
  self.rsyncUtil(fileToSort, ":" + self.data("sortFile"))
293
295
  os.unlink(fileToSort)
294
296
 
@@ -501,10 +503,11 @@ class AWSAutoscaleTestMultipleNodeTypes(AbstractAWSAutoscaleTest):
501
503
  sseKeyFile = os.path.join(os.getcwd(), "keyFile")
502
504
  with open(sseKeyFile, "w") as f:
503
505
  f.write("01234567890123456789012345678901")
504
- self.rsyncUtil(
505
- os.path.join(self._projectRootPath(), "src/toil/test/sort/sort.py"),
506
- ":" + self.script(),
507
- )
506
+ with get_data("test/sort/sort.py") as sort_py:
507
+ self.rsyncUtil(
508
+ sort_py,
509
+ ":" + self.script(),
510
+ )
508
511
  self.rsyncUtil(sseKeyFile, ":" + self.data("keyFile"))
509
512
  os.unlink(sseKeyFile)
510
513
 
@@ -523,7 +526,7 @@ class AWSAutoscaleTestMultipleNodeTypes(AbstractAWSAutoscaleTest):
523
526
  runCommand = [
524
527
  self.python(),
525
528
  self.script(),
526
- "--fileToSort=/home/s3am/bin/asadmin",
529
+ "--fileToSort=/etc/passwd",
527
530
  "--sortMemory=0.6G",
528
531
  "--mergeMemory=3.0G",
529
532
  ]
@@ -31,6 +31,8 @@ from toil.test import (
31
31
  slow,
32
32
  )
33
33
 
34
+ from toil.test.cwl.cwlTest import TestCWLv12Conformance
35
+
34
36
  log = logging.getLogger(__name__)
35
37
 
36
38
 
@@ -39,7 +41,7 @@ log = logging.getLogger(__name__)
39
41
  class AbstractClusterTest(ToilTest):
40
42
  def __init__(self, methodName: str) -> None:
41
43
  super().__init__(methodName=methodName)
42
- self.keyName = os.getenv("TOIL_AWS_KEYNAME").strip() or "id_rsa"
44
+ self.keyName = os.getenv("TOIL_AWS_KEYNAME", "id_rsa").strip()
43
45
  self.clusterName = f"aws-provisioner-test-{uuid4()}"
44
46
  self.leaderNodeType = "t2.medium"
45
47
  self.clusterType = "mesos"
@@ -237,6 +239,17 @@ class CWLOnARMTest(AbstractClusterTest):
237
239
 
238
240
  @needs_env_var("CI_COMMIT_SHA", "a git commit sha")
239
241
  def test_cwl_on_arm(self) -> None:
242
+ # Import the test we want to run remotely, so we know right away if it exists.
243
+ test_class = TestCWLv12Conformance
244
+ test_method = test_class.test_run_conformance
245
+
246
+ # Work out how to describe it as a pytest test spec.
247
+ # __qualname__ gives classname.methodname
248
+ test_name = test_method.__qualname__.replace(".", "::")
249
+ # The module path is the file path under src, with dots.
250
+ test_path = test_class.__module__.replace(".", "/")
251
+ test_spec = f"src/{test_path}.py::{test_name}"
252
+
240
253
  # Make a cluster
241
254
  self.launchCluster()
242
255
  # get the leader so we know the IP address - we don't need to wait since create cluster
@@ -276,12 +289,12 @@ class CWLOnARMTest(AbstractClusterTest):
276
289
  ]
277
290
  )
278
291
 
279
- # Runs the CWLv12Test on an ARM instance
292
+ # Runs the test on an ARM instance
280
293
  self.sshUtil(
281
294
  [
282
295
  "bash",
283
296
  "-c",
284
- f". .{self.venvDir}/bin/activate && cd {self.cwl_test_dir}/toil && pytest --log-cli-level DEBUG -r s src/toil/test/cwl/cwlTest.py::CWLv12Test::test_run_conformance",
297
+ f". .{self.venvDir}/bin/activate && cd {self.cwl_test_dir}/toil && pytest --log-cli-level DEBUG -r s {test_spec}",
285
298
  ]
286
299
  )
287
300
 
@@ -289,5 +302,5 @@ class CWLOnARMTest(AbstractClusterTest):
289
302
  # Bring it back to be an artifact.
290
303
  self.rsync_util(
291
304
  f":{self.cwl_test_dir}/toil/conformance-1.2.junit.xml",
292
- os.path.join(self._projectRootPath(), "arm-conformance-1.2.junit.xml"),
305
+ str(self._rootpath / "arm-conformance-1.2.junit.xml"),
293
306
  )
@@ -21,6 +21,7 @@ import pytest
21
21
 
22
22
  from toil.test import (
23
23
  ToilTest,
24
+ get_data,
24
25
  integrative,
25
26
  needs_fetchable_appliance,
26
27
  needs_google_project,
@@ -214,10 +215,11 @@ class GCEAutoscaleTest(AbstractGCEAutoscaleTest):
214
215
  with open(fileToSort, "w") as f:
215
216
  # Fixme: making this file larger causes the test to hang
216
217
  f.write("01234567890123456789012345678901")
217
- self.rsyncUtil(
218
- os.path.join(self._projectRootPath(), "src/toil/test/sort/sort.py"),
219
- ":/home/sort.py",
220
- )
218
+ with get_data("test/sort/sort.py") as sort_py:
219
+ self.rsyncUtil(
220
+ sort_py,
221
+ ":/home/sort.py",
222
+ )
221
223
  self.rsyncUtil(fileToSort, ":/home/sortFile")
222
224
  os.unlink(fileToSort)
223
225
 
@@ -324,10 +326,11 @@ class GCEAutoscaleTestMultipleNodeTypes(AbstractGCEAutoscaleTest):
324
326
  sseKeyFile = os.path.join(os.getcwd(), "keyFile")
325
327
  with open(sseKeyFile, "w") as f:
326
328
  f.write("01234567890123456789012345678901")
327
- self.rsyncUtil(
328
- os.path.join(self._projectRootPath(), "src/toil/test/sort/sort.py"),
329
- ":/home/sort.py",
330
- )
329
+ with get_data("test/sort/sort.py") as sort_py:
330
+ self.rsyncUtil(
331
+ sort_py,
332
+ ":/home/sort.py",
333
+ )
331
334
  self.rsyncUtil(sseKeyFile, ":/home/keyFile")
332
335
  os.unlink(sseKeyFile)
333
336
 
@@ -338,7 +341,7 @@ class GCEAutoscaleTestMultipleNodeTypes(AbstractGCEAutoscaleTest):
338
341
  runCommand = [
339
342
  "/home/venv/bin/python",
340
343
  "/home/sort.py",
341
- "--fileToSort=/home/s3am/bin/asadmin",
344
+ "--fileToSort=/etc/passwd",
342
345
  "--sortMemory=0.6G",
343
346
  "--mergeMemory=3.0G",
344
347
  ]
@@ -376,12 +379,11 @@ class GCERestartTest(AbstractGCEAutoscaleTest):
376
379
  self.jobStore = f"google:{self.projectID}:restart-{uuid4()}"
377
380
 
378
381
  def _getScript(self):
379
- self.rsyncUtil(
380
- os.path.join(
381
- self._projectRootPath(), "src/toil/test/provisioners/restartScript.py"
382
- ),
383
- ":" + self.scriptName,
384
- )
382
+ with get_data("test/provisioners/restartScript.py") as restartScript:
383
+ self.rsyncUtil(
384
+ restartScript,
385
+ ":" + self.scriptName,
386
+ )
385
387
 
386
388
  def _runScript(self, toilOptions):
387
389
  # clean = onSuccess