toil 8.0.0__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 (270) hide show
  1. toil/__init__.py +4 -39
  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/options.py +1 -0
  11. toil/batchSystems/singleMachine.py +1 -1
  12. toil/batchSystems/slurm.py +229 -84
  13. toil/bus.py +5 -3
  14. toil/common.py +198 -54
  15. toil/cwl/cwltoil.py +32 -11
  16. toil/job.py +110 -86
  17. toil/jobStores/abstractJobStore.py +24 -3
  18. toil/jobStores/aws/jobStore.py +46 -10
  19. toil/jobStores/fileJobStore.py +25 -1
  20. toil/jobStores/googleJobStore.py +104 -30
  21. toil/leader.py +9 -0
  22. toil/lib/accelerators.py +3 -1
  23. toil/lib/aws/session.py +14 -3
  24. toil/lib/aws/utils.py +92 -35
  25. toil/lib/aws/utils.py.orig +504 -0
  26. toil/lib/bioio.py +1 -1
  27. toil/lib/docker.py +252 -91
  28. toil/lib/dockstore.py +387 -0
  29. toil/lib/ec2nodes.py +3 -2
  30. toil/lib/exceptions.py +5 -3
  31. toil/lib/history.py +1345 -0
  32. toil/lib/history_submission.py +695 -0
  33. toil/lib/io.py +56 -23
  34. toil/lib/misc.py +25 -1
  35. toil/lib/resources.py +2 -1
  36. toil/lib/retry.py +10 -10
  37. toil/lib/threading.py +11 -10
  38. toil/lib/{integration.py → trs.py} +95 -46
  39. toil/lib/web.py +38 -0
  40. toil/options/common.py +25 -2
  41. toil/options/cwl.py +10 -0
  42. toil/options/wdl.py +11 -0
  43. toil/provisioners/gceProvisioner.py +4 -4
  44. toil/server/api_spec/LICENSE +201 -0
  45. toil/server/api_spec/README.rst +5 -0
  46. toil/server/cli/wes_cwl_runner.py +5 -4
  47. toil/server/utils.py +2 -3
  48. toil/statsAndLogging.py +35 -1
  49. toil/test/__init__.py +275 -115
  50. toil/test/batchSystems/batchSystemTest.py +227 -205
  51. toil/test/batchSystems/test_slurm.py +199 -2
  52. toil/test/cactus/pestis.tar.gz +0 -0
  53. toil/test/conftest.py +7 -0
  54. toil/test/cwl/2.fasta +11 -0
  55. toil/test/cwl/2.fastq +12 -0
  56. toil/test/cwl/conftest.py +39 -0
  57. toil/test/cwl/cwlTest.py +1015 -780
  58. toil/test/cwl/directory/directory/file.txt +15 -0
  59. toil/test/cwl/download_directory_file.json +4 -0
  60. toil/test/cwl/download_directory_s3.json +4 -0
  61. toil/test/cwl/download_file.json +6 -0
  62. toil/test/cwl/download_http.json +6 -0
  63. toil/test/cwl/download_https.json +6 -0
  64. toil/test/cwl/download_s3.json +6 -0
  65. toil/test/cwl/download_subdirectory_file.json +5 -0
  66. toil/test/cwl/download_subdirectory_s3.json +5 -0
  67. toil/test/cwl/empty.json +1 -0
  68. toil/test/cwl/mock_mpi/fake_mpi.yml +8 -0
  69. toil/test/cwl/mock_mpi/fake_mpi_run.py +42 -0
  70. toil/test/cwl/optional-file-exists.json +6 -0
  71. toil/test/cwl/optional-file-missing.json +6 -0
  72. toil/test/cwl/optional-file.cwl +18 -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/lib/aws/test_iam.py +3 -1
  123. toil/test/lib/dockerTest.py +205 -122
  124. toil/test/lib/test_history.py +236 -0
  125. toil/test/lib/test_trs.py +161 -0
  126. toil/test/provisioners/aws/awsProvisionerTest.py +12 -9
  127. toil/test/provisioners/clusterTest.py +4 -4
  128. toil/test/provisioners/gceProvisionerTest.py +16 -14
  129. toil/test/sort/sort.py +4 -1
  130. toil/test/src/busTest.py +17 -17
  131. toil/test/src/deferredFunctionTest.py +145 -132
  132. toil/test/src/importExportFileTest.py +71 -63
  133. toil/test/src/jobEncapsulationTest.py +27 -28
  134. toil/test/src/jobServiceTest.py +149 -133
  135. toil/test/src/jobTest.py +219 -211
  136. toil/test/src/miscTests.py +66 -60
  137. toil/test/src/promisedRequirementTest.py +163 -169
  138. toil/test/src/regularLogTest.py +24 -24
  139. toil/test/src/resourceTest.py +82 -76
  140. toil/test/src/restartDAGTest.py +51 -47
  141. toil/test/src/resumabilityTest.py +24 -19
  142. toil/test/src/retainTempDirTest.py +60 -57
  143. toil/test/src/systemTest.py +17 -13
  144. toil/test/src/threadingTest.py +29 -32
  145. toil/test/utils/ABCWorkflowDebug/B_file.txt +1 -0
  146. toil/test/utils/ABCWorkflowDebug/debugWorkflow.py +204 -0
  147. toil/test/utils/ABCWorkflowDebug/mkFile.py +16 -0
  148. toil/test/utils/ABCWorkflowDebug/sleep.cwl +12 -0
  149. toil/test/utils/ABCWorkflowDebug/sleep.yaml +1 -0
  150. toil/test/utils/toilDebugTest.py +117 -102
  151. toil/test/utils/toilKillTest.py +54 -53
  152. toil/test/utils/utilsTest.py +303 -229
  153. toil/test/wdl/lint_error.wdl +9 -0
  154. toil/test/wdl/md5sum/empty_file.json +1 -0
  155. toil/test/wdl/md5sum/md5sum-gs.json +1 -0
  156. toil/test/wdl/md5sum/md5sum.1.0.wdl +32 -0
  157. toil/test/wdl/md5sum/md5sum.input +1 -0
  158. toil/test/wdl/md5sum/md5sum.json +1 -0
  159. toil/test/wdl/md5sum/md5sum.wdl +25 -0
  160. toil/test/wdl/miniwdl_self_test/inputs-namespaced.json +1 -0
  161. toil/test/wdl/miniwdl_self_test/inputs.json +1 -0
  162. toil/test/wdl/miniwdl_self_test/self_test.wdl +40 -0
  163. toil/test/wdl/standard_library/as_map.json +16 -0
  164. toil/test/wdl/standard_library/as_map_as_input.wdl +23 -0
  165. toil/test/wdl/standard_library/as_pairs.json +7 -0
  166. toil/test/wdl/standard_library/as_pairs_as_input.wdl +23 -0
  167. toil/test/wdl/standard_library/ceil.json +3 -0
  168. toil/test/wdl/standard_library/ceil_as_command.wdl +16 -0
  169. toil/test/wdl/standard_library/ceil_as_input.wdl +16 -0
  170. toil/test/wdl/standard_library/collect_by_key.json +1 -0
  171. toil/test/wdl/standard_library/collect_by_key_as_input.wdl +23 -0
  172. toil/test/wdl/standard_library/cross.json +11 -0
  173. toil/test/wdl/standard_library/cross_as_input.wdl +19 -0
  174. toil/test/wdl/standard_library/flatten.json +7 -0
  175. toil/test/wdl/standard_library/flatten_as_input.wdl +18 -0
  176. toil/test/wdl/standard_library/floor.json +3 -0
  177. toil/test/wdl/standard_library/floor_as_command.wdl +16 -0
  178. toil/test/wdl/standard_library/floor_as_input.wdl +16 -0
  179. toil/test/wdl/standard_library/keys.json +8 -0
  180. toil/test/wdl/standard_library/keys_as_input.wdl +24 -0
  181. toil/test/wdl/standard_library/length.json +7 -0
  182. toil/test/wdl/standard_library/length_as_input.wdl +16 -0
  183. toil/test/wdl/standard_library/length_as_input_with_map.json +7 -0
  184. toil/test/wdl/standard_library/length_as_input_with_map.wdl +17 -0
  185. toil/test/wdl/standard_library/length_invalid.json +3 -0
  186. toil/test/wdl/standard_library/range.json +3 -0
  187. toil/test/wdl/standard_library/range_0.json +3 -0
  188. toil/test/wdl/standard_library/range_as_input.wdl +17 -0
  189. toil/test/wdl/standard_library/range_invalid.json +3 -0
  190. toil/test/wdl/standard_library/read_boolean.json +3 -0
  191. toil/test/wdl/standard_library/read_boolean_as_command.wdl +17 -0
  192. toil/test/wdl/standard_library/read_float.json +3 -0
  193. toil/test/wdl/standard_library/read_float_as_command.wdl +17 -0
  194. toil/test/wdl/standard_library/read_int.json +3 -0
  195. toil/test/wdl/standard_library/read_int_as_command.wdl +17 -0
  196. toil/test/wdl/standard_library/read_json.json +3 -0
  197. toil/test/wdl/standard_library/read_json_as_output.wdl +31 -0
  198. toil/test/wdl/standard_library/read_lines.json +3 -0
  199. toil/test/wdl/standard_library/read_lines_as_output.wdl +31 -0
  200. toil/test/wdl/standard_library/read_map.json +3 -0
  201. toil/test/wdl/standard_library/read_map_as_output.wdl +31 -0
  202. toil/test/wdl/standard_library/read_string.json +3 -0
  203. toil/test/wdl/standard_library/read_string_as_command.wdl +17 -0
  204. toil/test/wdl/standard_library/read_tsv.json +3 -0
  205. toil/test/wdl/standard_library/read_tsv_as_output.wdl +31 -0
  206. toil/test/wdl/standard_library/round.json +3 -0
  207. toil/test/wdl/standard_library/round_as_command.wdl +16 -0
  208. toil/test/wdl/standard_library/round_as_input.wdl +16 -0
  209. toil/test/wdl/standard_library/size.json +3 -0
  210. toil/test/wdl/standard_library/size_as_command.wdl +17 -0
  211. toil/test/wdl/standard_library/size_as_output.wdl +36 -0
  212. toil/test/wdl/standard_library/stderr.json +3 -0
  213. toil/test/wdl/standard_library/stderr_as_output.wdl +30 -0
  214. toil/test/wdl/standard_library/stdout.json +3 -0
  215. toil/test/wdl/standard_library/stdout_as_output.wdl +30 -0
  216. toil/test/wdl/standard_library/sub.json +3 -0
  217. toil/test/wdl/standard_library/sub_as_input.wdl +17 -0
  218. toil/test/wdl/standard_library/sub_as_input_with_file.wdl +17 -0
  219. toil/test/wdl/standard_library/transpose.json +6 -0
  220. toil/test/wdl/standard_library/transpose_as_input.wdl +18 -0
  221. toil/test/wdl/standard_library/write_json.json +6 -0
  222. toil/test/wdl/standard_library/write_json_as_command.wdl +17 -0
  223. toil/test/wdl/standard_library/write_lines.json +7 -0
  224. toil/test/wdl/standard_library/write_lines_as_command.wdl +17 -0
  225. toil/test/wdl/standard_library/write_map.json +6 -0
  226. toil/test/wdl/standard_library/write_map_as_command.wdl +17 -0
  227. toil/test/wdl/standard_library/write_tsv.json +6 -0
  228. toil/test/wdl/standard_library/write_tsv_as_command.wdl +17 -0
  229. toil/test/wdl/standard_library/zip.json +12 -0
  230. toil/test/wdl/standard_library/zip_as_input.wdl +19 -0
  231. toil/test/wdl/test.csv +3 -0
  232. toil/test/wdl/test.tsv +3 -0
  233. toil/test/wdl/testfiles/croo.wdl +38 -0
  234. toil/test/wdl/testfiles/drop_files.wdl +62 -0
  235. toil/test/wdl/testfiles/drop_files_subworkflow.wdl +13 -0
  236. toil/test/wdl/testfiles/empty.txt +0 -0
  237. toil/test/wdl/testfiles/not_enough_outputs.wdl +33 -0
  238. toil/test/wdl/testfiles/random.wdl +66 -0
  239. toil/test/wdl/testfiles/string_file_coercion.json +1 -0
  240. toil/test/wdl/testfiles/string_file_coercion.wdl +35 -0
  241. toil/test/wdl/testfiles/test.json +4 -0
  242. toil/test/wdl/testfiles/test_boolean.txt +1 -0
  243. toil/test/wdl/testfiles/test_float.txt +1 -0
  244. toil/test/wdl/testfiles/test_int.txt +1 -0
  245. toil/test/wdl/testfiles/test_lines.txt +5 -0
  246. toil/test/wdl/testfiles/test_map.txt +2 -0
  247. toil/test/wdl/testfiles/test_string.txt +1 -0
  248. toil/test/wdl/testfiles/url_to_file.wdl +13 -0
  249. toil/test/wdl/testfiles/url_to_optional_file.wdl +13 -0
  250. toil/test/wdl/testfiles/vocab.json +1 -0
  251. toil/test/wdl/testfiles/vocab.wdl +66 -0
  252. toil/test/wdl/testfiles/wait.wdl +34 -0
  253. toil/test/wdl/wdl_specification/type_pair.json +23 -0
  254. toil/test/wdl/wdl_specification/type_pair_basic.wdl +36 -0
  255. toil/test/wdl/wdl_specification/type_pair_with_files.wdl +36 -0
  256. toil/test/wdl/wdl_specification/v1_spec.json +1 -0
  257. toil/test/wdl/wdl_specification/v1_spec_declaration.wdl +39 -0
  258. toil/test/wdl/wdltoil_test.py +681 -408
  259. toil/test/wdl/wdltoil_test_kubernetes.py +2 -2
  260. toil/version.py +10 -10
  261. toil/wdl/wdltoil.py +350 -123
  262. toil/worker.py +113 -33
  263. {toil-8.0.0.dist-info → toil-8.2.0.dist-info}/METADATA +13 -7
  264. toil-8.2.0.dist-info/RECORD +439 -0
  265. {toil-8.0.0.dist-info → toil-8.2.0.dist-info}/WHEEL +1 -1
  266. toil/test/lib/test_integration.py +0 -104
  267. toil-8.0.0.dist-info/RECORD +0 -253
  268. {toil-8.0.0.dist-info → toil-8.2.0.dist-info}/entry_points.txt +0 -0
  269. {toil-8.0.0.dist-info → toil-8.2.0.dist-info/licenses}/LICENSE +0 -0
  270. {toil-8.0.0.dist-info → toil-8.2.0.dist-info}/top_level.txt +0 -0
toil/lib/dockstore.py ADDED
@@ -0,0 +1,387 @@
1
+ # Copyright (C) 2024 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
+
15
+ """
16
+ Contains functions for integrating Toil with UCSC Dockstore, for reporting metrics.
17
+
18
+ For basic TRS functionality for fetching workflows, see trs.py.
19
+ """
20
+
21
+ import datetime
22
+ import logging
23
+ import math
24
+ import os
25
+ import re
26
+ import sys
27
+ import uuid
28
+ from typing import Any, Literal, Optional, Union, TypedDict, cast
29
+
30
+ from urllib.parse import urlparse, unquote, quote
31
+ import requests
32
+
33
+ from toil.lib.misc import unix_seconds_to_timestamp, seconds_to_duration
34
+ from toil.lib.trs import TRS_ROOT
35
+ from toil.lib.retry import retry
36
+ from toil.lib.web import web_session
37
+ from toil.version import baseVersion
38
+
39
+ if sys.version_info < (3, 11):
40
+ from typing_extensions import NotRequired
41
+ else:
42
+ from typing import NotRequired
43
+
44
+ logger = logging.getLogger(__name__)
45
+
46
+ # We assume TRS_ROOT is actually a Dockstore instance.
47
+
48
+ # This is a publish-able token for production Dockstore for Toil to use.
49
+ # This is NOT a secret value.
50
+ DEFAULT_DOCKSTORE_TOKEN = "2bff46294daddef6df185452b04db6143ea8a59f52ee3c325d3e1df418511b7d"
51
+
52
+ # How should we authenticate our Dockstore requests?
53
+ DOCKSTORE_TOKEN = os.environ.get("TOIL_DOCKSTORE_TOKEN", DEFAULT_DOCKSTORE_TOKEN)
54
+
55
+ # What platform should we report metrics as?
56
+ DOCKSTORE_PLATFORM = "TOIL"
57
+
58
+
59
+ # This is a https://schema.org/CompletedActionStatus
60
+ # The values here are from expanding the type info in the Docksotre docs at
61
+ # <https://dockstore.org/api/static/swagger-ui/index.html#/extendedGA4GH/executionMetricsPost>
62
+ ExecutionStatus = Union[Literal["ALL"], Literal["SUCCESSFUL"], Literal["FAILED"], Literal["FAILED_SEMANTIC_INVALID"], Literal["FAILED_RUNTIME_INVALID"], Literal["ABORTED"]]
63
+
64
+ class Cost(TypedDict):
65
+ """
66
+ Representation of the cost of running something.
67
+ """
68
+
69
+ value: float
70
+ """
71
+ Cost in US Dollars.
72
+ """
73
+
74
+ class RunExecution(TypedDict):
75
+ """
76
+ Dockstore metrics data for a workflow or task run.
77
+ """
78
+
79
+ executionId: str
80
+ """
81
+ Executor-generated unique execution ID for this workflow or task.
82
+ """
83
+
84
+ dateExecuted: str
85
+ """
86
+ ISO 8601 UTC timestamp when the execution happend.
87
+ """
88
+
89
+ executionStatus: ExecutionStatus
90
+ """
91
+ Did the execution work?
92
+ """
93
+
94
+ executionTime: NotRequired[str]
95
+ """
96
+ Total time of the run in ISO 8601 duration format.
97
+ """
98
+
99
+ # TODO: Is this meant to be actual usage or amount provided?
100
+ memoryRequirementsGB: NotRequired[float]
101
+ """
102
+ Memory required for the execution in gigabytes (not GiB).
103
+ """
104
+
105
+ cpuRequirements: NotRequired[int]
106
+ """
107
+ Number of CPUs required.
108
+ """
109
+
110
+ cost: NotRequired[Cost]
111
+ """
112
+ How much the execution cost to run.
113
+ """
114
+
115
+ # TODO: What if two cloud providers have the same region naming scheme?
116
+ region: NotRequired[str]
117
+ """
118
+ The (cloud) region the workflow was executed in.
119
+ """
120
+
121
+ additionalProperties: NotRequired[dict[str, Any]]
122
+ """
123
+ Any additional properties to send.
124
+
125
+ Dockstore can take any JSON-able structured data.
126
+ """
127
+
128
+ class TaskExecutions(TypedDict):
129
+ """
130
+ Dockstore metrics data for all the tasks in a workflow.
131
+ """
132
+
133
+ # TODO: Right now we use different IDs for the workflow RunExecution and
134
+ # for its corresponding collection of TaskExecutions, so there's no nice
135
+ # way to find the one from the other.
136
+ executionId: str
137
+ """
138
+ Executor-generated unique execution ID.
139
+ """
140
+
141
+ dateExecuted: str
142
+ """
143
+ ISO 8601 UTC timestamp when the execution happend.
144
+ """
145
+
146
+ taskExecutions: list[RunExecution]
147
+ """
148
+ Individual executions of each task in the workflow.
149
+ """
150
+
151
+ additionalProperties: NotRequired[dict[str, Any]]
152
+ """
153
+ Any additional properties to send.
154
+
155
+ Dockstore can take any JSON-able structured data.
156
+ """
157
+
158
+ def ensure_valid_id(execution_id: str) -> None:
159
+ """
160
+ Make sure the given execution ID is in Dockstore format and will be accepted by Dockstore.
161
+
162
+ Must be alphanumeric (with internal underscores allowed) and <100
163
+ characters long.
164
+
165
+ :raises ValueError: if the ID is not in the right format
166
+ """
167
+ if len(execution_id) >= 100:
168
+ raise ValueError("Execution ID too long")
169
+ if len(execution_id) == 0:
170
+ raise ValueError("Execution ID must not be empty")
171
+ if execution_id[0] == "_" or execution_id[-1] == "_":
172
+ raise ValueError("Execution ID must not start or end with an underscore")
173
+ if not re.fullmatch("[a-zA-Z0-9_]+", execution_id):
174
+ raise ValueError("Execution ID must be alphanumeric with internal underscores")
175
+
176
+ def pack_workflow_metrics(
177
+ execution_id: str,
178
+ start_time: float,
179
+ runtime: float,
180
+ succeeded: bool,
181
+ job_store_type: Optional[str] = None,
182
+ batch_system: Optional[str] = None,
183
+ caching: Optional[bool] = None,
184
+ toil_version: Optional[str] = None,
185
+ python_version: Optional[str] = None,
186
+ platform_system: Optional[str] = None,
187
+ platform_machine: Optional[str] = None
188
+ ) -> RunExecution:
189
+ """
190
+ Pack up per-workflow metrics into a format that can be submitted to Dockstore.
191
+
192
+ :param execution_id: Unique ID for the workflow execution. Must be in
193
+ Dockstore format.
194
+ :param start_time: Execution start time in seconds since the Unix epoch.
195
+ :param rutime: Execution duration in seconds.
196
+ :param jobstore_type: Kind of job store used, like "file" or "aws".
197
+ :param batch_system: Python class name implementing the batch system used.
198
+ :param caching: Whether Toil filestore-level cahcing was used.
199
+ :param toil_version: Version of Toil used (without any Git hash).
200
+ :param python_version: Version of Python used.
201
+ :param platform_system: Operating system type (like "Darwin" or "Linux").
202
+ :param platform_machine: Machine architecture of the leader (like "AMD64").
203
+ """
204
+
205
+ # Enforce Dockstore's constraints
206
+ ensure_valid_id(execution_id)
207
+
208
+ # Pack up into a RunExecution
209
+ result = RunExecution(
210
+ executionId=execution_id,
211
+ dateExecuted=unix_seconds_to_timestamp(start_time),
212
+ executionTime=seconds_to_duration(runtime),
213
+ executionStatus="SUCCESSFUL" if succeeded else "FAILED"
214
+ )
215
+
216
+ # TODO: Just use kwargs here?
217
+ additional_properties: dict[str, Any] = {}
218
+
219
+ if job_store_type is not None:
220
+ additional_properties["jobStoreType"] = job_store_type
221
+
222
+ if batch_system is not None:
223
+ additional_properties["batchSystem"] = batch_system
224
+
225
+ if caching is not None:
226
+ additional_properties["caching"] = caching
227
+
228
+ if toil_version is not None:
229
+ additional_properties["toilVersion"] = toil_version
230
+
231
+ if python_version is not None:
232
+ additional_properties["pythonVersion"] = python_version
233
+
234
+ if platform_system is not None:
235
+ additional_properties["platformSystem"] = platform_system
236
+
237
+ if platform_machine is not None:
238
+ additional_properties["platformMachine"] = platform_machine
239
+
240
+ if len(additional_properties) > 0:
241
+ result["additionalProperties"] = additional_properties
242
+
243
+ return result
244
+
245
+ def pack_single_task_metrics(
246
+ execution_id: str,
247
+ start_time: float,
248
+ runtime: float,
249
+ succeeded: bool,
250
+ job_name: Optional[str] = None,
251
+ cores: Optional[float] = None,
252
+ cpu_seconds: Optional[float] = None,
253
+ memory_bytes: Optional[int] = None,
254
+ disk_bytes: Optional[int] = None,
255
+ ) -> RunExecution:
256
+ """
257
+ Pack up metrics for a single task execution in a format that can be used in a Dockstore submission.
258
+
259
+ :param execution_id: Unique ID for the workflow execution. Must be in
260
+ Dockstore format.
261
+ :param start_time: Execution start time in seconds since the Unix epoch.
262
+ :param rutime: Execution duration in seconds.
263
+ :param succeeded: Whether the execution succeeded.
264
+ :param job_name: Name of the job within the workflow.
265
+ :param cores: CPU cores allocated to the job.
266
+ :param cpu_seconds: CPU seconds consumed by the job.
267
+ :param memory_bytes: Memory consumed by the job in bytes.
268
+ :param disk_bytes: Disk space consumed by the job in bytes.
269
+ """
270
+
271
+ # TODO: Deduplicate with workflow code since the output type is the same.
272
+
273
+ # Enforce Dockstore's constraints
274
+ ensure_valid_id(execution_id)
275
+
276
+ # Pack up into a RunExecution
277
+ result = RunExecution(
278
+ executionId=execution_id,
279
+ dateExecuted=unix_seconds_to_timestamp(start_time),
280
+ executionTime=seconds_to_duration(runtime),
281
+ executionStatus="SUCCESSFUL" if succeeded else "FAILED"
282
+ )
283
+
284
+ if memory_bytes is not None:
285
+ # Convert bytes to fractional gigabytes
286
+ result["memoryRequirementsGB"] = memory_bytes / 1_000_000_000
287
+
288
+ if cores is not None:
289
+ # Convert possibly fractional cores to an integer for Dockstore
290
+ result["cpuRequirements"] = int(math.ceil(cores))
291
+
292
+ # TODO: Just use kwargs here?
293
+ additional_properties: dict[str, Any] = {}
294
+
295
+ if job_name is not None:
296
+ # Convert to Doskstore-style camelCase property keys
297
+ additional_properties["jobName"] = job_name
298
+
299
+ if disk_bytes is not None:
300
+ # Convert to a Dockstore-style fractional disk gigabytes
301
+ additional_properties["diskRequirementsGB"] = disk_bytes / 1_000_000_000
302
+
303
+ if cpu_seconds is not None:
304
+ # Use a Dockstore-ier name here too
305
+ additional_properties["cpuRequirementsCoreSeconds"] = cpu_seconds
306
+
307
+ if len(additional_properties) > 0:
308
+ result["additionalProperties"] = additional_properties
309
+
310
+ return result
311
+
312
+
313
+ def pack_workflow_task_set_metrics(execution_id: str, start_time: float, tasks: list[RunExecution]) -> TaskExecutions:
314
+ """
315
+ Pack up metrics for all the tasks in a workflow execution into a format that can be submitted to Dockstore.
316
+
317
+ :param execution_id: Unique ID for the workflow execution. Must be in
318
+ Dockstore format.
319
+ :param start_time: Execution start time for the overall workflow execution
320
+ in seconds since the Unix epoch.
321
+ :param tasks: Packed tasks from pack_single_task_metrics()
322
+ """
323
+
324
+ # Enforce Dockstore's constraints
325
+ ensure_valid_id(execution_id)
326
+
327
+ return TaskExecutions(
328
+ executionId=execution_id,
329
+ dateExecuted=unix_seconds_to_timestamp(start_time),
330
+ taskExecutions=tasks
331
+ )
332
+
333
+ def send_metrics(trs_workflow_id: str, trs_version: str, workflow_runs: list[RunExecution], workflow_task_sets: list[TaskExecutions]) -> None:
334
+ """
335
+ Send packed workflow and/or task metrics to Dockstore.
336
+
337
+ :param workflow_runs: list of packed metrics objects for each workflow.
338
+
339
+ :param workflow_task_sets: list of packed metrics objects for the tasks in
340
+ each workflow. Each workflow should have one entry containing all its
341
+ tasks. Does not have to be the same order/set of workflows as
342
+ workflow_runs.
343
+
344
+ :raises requests.HTTPError: if Dockstore does not accept the metrics.
345
+ """
346
+
347
+ # Aggregate into a submission
348
+ to_post = {
349
+ "runExecutions": workflow_runs,
350
+ "taskExecutions": workflow_task_sets,
351
+ "validationExecutions": []
352
+ }
353
+
354
+ # Set the submission query string metadata
355
+ submission_params = {
356
+ "platform": DOCKSTORE_PLATFORM,
357
+ "description": "Workflow status from Toil"
358
+ }
359
+
360
+ # Set the headers. Even though user agent isn't in here, it still gets
361
+ # sent.
362
+ headers = {}
363
+ if DOCKSTORE_TOKEN is not None:
364
+ headers["Authorization"] = f"Bearer {DOCKSTORE_TOKEN}"
365
+
366
+ # Note that Dockstore's metrics apparently need two levels of /api for some reason.
367
+ endpoint_url = f"{TRS_ROOT}/api/api/ga4gh/v2/extended/{quote(trs_workflow_id, safe='')}/versions/{quote(trs_version, safe='')}/executions"
368
+
369
+ logger.info("Sending workflow metrics to %s", endpoint_url)
370
+ logger.debug("With data: %s", to_post)
371
+ logger.debug("With headers: %s", headers)
372
+
373
+ try:
374
+ result = web_session.post(endpoint_url, params=submission_params, json=to_post, headers=headers)
375
+ result.raise_for_status()
376
+ logger.debug("Workflow metrics were accepted by Dockstore. Dockstore response code: %s", result.status_code)
377
+ except requests.HTTPError as e:
378
+ logger.warning("Workflow metrics were not accepted by Dockstore. Dockstore complained: %s", e.response.text)
379
+ raise
380
+
381
+ def get_metrics_url(trs_workflow_id: str, trs_version: str, execution_id: str) -> str:
382
+ """
383
+ Get the URL where a workflow metrics object (for a workflow, or for a set of tasks) can be fetched back from.
384
+ """
385
+
386
+ return f"{TRS_ROOT}/api/api/ga4gh/v2/extended/{quote(trs_workflow_id, safe='')}/versions/{quote(trs_version, safe='')}/execution?platform={DOCKSTORE_PLATFORM}&executionId={quote(execution_id, safe='')}"
387
+
toil/lib/ec2nodes.py CHANGED
@@ -21,7 +21,8 @@ import textwrap
21
21
  from typing import Any, Union
22
22
 
23
23
  import enlighten # type: ignore
24
- import requests
24
+
25
+ from toil.lib.web import web_session
25
26
 
26
27
  logger = logging.getLogger(__name__)
27
28
  manager = enlighten.get_manager()
@@ -193,7 +194,7 @@ def download_region_json(filename: str, region: str = "us-east-1") -> None:
193
194
  aws instance name (example: 't2.micro'), and the value is an
194
195
  InstanceType object representing that aws instance name.
195
196
  """
196
- response = requests.get(
197
+ response = web_session.get(
197
198
  f"https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/AmazonEC2/current/{region}/index.json",
198
199
  stream=True,
199
200
  )
toil/lib/exceptions.py CHANGED
@@ -15,6 +15,8 @@
15
15
  # 5.14.2018: copied into Toil from https://github.com/BD2KGenomics/bd2k-python-lib
16
16
 
17
17
  import sys
18
+ from typing import Optional
19
+ import logging
18
20
  from urllib.parse import ParseResult
19
21
 
20
22
 
@@ -39,15 +41,15 @@ class panic:
39
41
  the primary exception will be reraised.
40
42
  """
41
43
 
42
- def __init__(self, log=None):
44
+ def __init__(self, log: Optional[logging.Logger] = None) -> None:
43
45
  super().__init__()
44
46
  self.log = log
45
47
  self.exc_info = None
46
48
 
47
- def __enter__(self):
49
+ def __enter__(self) -> None:
48
50
  self.exc_info = sys.exc_info()
49
51
 
50
- def __exit__(self, *exc_info):
52
+ def __exit__(self, *exc_info) -> None:
51
53
  if self.log is not None and exc_info and exc_info[0]:
52
54
  self.log.warning("Exception during panic", exc_info=exc_info)
53
55
  exc_type, exc_value, traceback = self.exc_info