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
@@ -32,6 +32,7 @@ from typing import (
32
32
  Union,
33
33
  cast,
34
34
  overload,
35
+ Type,
35
36
  )
36
37
  from urllib.error import HTTPError
37
38
  from urllib.parse import ParseResult, urlparse
@@ -52,6 +53,7 @@ from toil.lib.exceptions import UnimplementedURLException
52
53
  from toil.lib.io import WriteWatchingStream
53
54
  from toil.lib.memoize import memoize
54
55
  from toil.lib.retry import ErrorCondition, retry
56
+ from toil.lib.url import URLAccess
55
57
 
56
58
  if TYPE_CHECKING:
57
59
  from toil.job import TemporaryID
@@ -354,23 +356,6 @@ class AbstractJobStore(ABC):
354
356
  jobStoreClasses.append(jobStoreClass)
355
357
  return jobStoreClasses
356
358
 
357
- @classmethod
358
- def _findJobStoreForUrl(
359
- cls, url: ParseResult, export: bool = False
360
- ) -> "AbstractJobStore":
361
- """
362
- Returns the AbstractJobStore subclass that supports the given URL.
363
-
364
- :param ParseResult url: The given URL
365
-
366
- :param bool export: Determines if the url is supported for exporting
367
-
368
- :rtype: toil.jobStore.AbstractJobStore
369
- """
370
- for implementation in cls._get_job_store_classes():
371
- if implementation._supports_url(url, export):
372
- return implementation
373
- raise UnimplementedURLException(url, "export" if export else "import")
374
359
 
375
360
  # Importing a file with a shared file name returns None, but without one it
376
361
  # returns a file ID. Explain this to MyPy.
@@ -464,7 +449,7 @@ class AbstractJobStore(ABC):
464
449
  # optimizations that circumvent this, the _import_file method should be overridden by
465
450
  # subclasses of AbstractJobStore.
466
451
  parseResult = urlparse(src_uri)
467
- otherCls = self._findJobStoreForUrl(parseResult)
452
+ otherCls = URLAccess._find_url_implementation(parseResult)
468
453
  logger.info("Importing input %s...", src_uri)
469
454
  return self._import_file(
470
455
  otherCls,
@@ -476,7 +461,7 @@ class AbstractJobStore(ABC):
476
461
 
477
462
  def _import_file(
478
463
  self,
479
- otherCls: "AbstractJobStore",
464
+ otherCls: Type["URLAccess"],
480
465
  uri: ParseResult,
481
466
  shared_file_name: Optional[str] = None,
482
467
  hardlink: bool = False,
@@ -490,7 +475,7 @@ class AbstractJobStore(ABC):
490
475
 
491
476
  Raises FileNotFoundError if the file does not exist.
492
477
 
493
- :param AbstractJobStore otherCls: The concrete subclass of AbstractJobStore that supports
478
+ :param URLAccess otherCls: The class of URLAccess that supports
494
479
  reading from the given URL and getting the file size from the URL.
495
480
 
496
481
  :param ParseResult uri: The location of the file to import.
@@ -535,16 +520,16 @@ class AbstractJobStore(ABC):
535
520
  from toil.common import Toil
536
521
  dst_uri = Toil.normalize_uri(dst_uri)
537
522
  parseResult = urlparse(dst_uri)
538
- otherCls = self._findJobStoreForUrl(parseResult, export=True)
523
+ otherCls = URLAccess._find_url_implementation(parseResult, export=True)
539
524
  self._export_file(otherCls, file_id, parseResult)
540
525
 
541
526
  def _export_file(
542
- self, otherCls: "AbstractJobStore", jobStoreFileID: FileID, url: ParseResult
527
+ self, otherCls: Type["URLAccess"], jobStoreFileID: FileID, url: ParseResult
543
528
  ) -> None:
544
529
  """
545
530
  Refer to exportFile docstring for information about this method.
546
531
 
547
- :param AbstractJobStore otherCls: The concrete subclass of AbstractJobStore that supports
532
+ :param URLAccess otherCls: The class of URLAccess that supports
548
533
  exporting to the given URL. Note that the type annotation here is not completely
549
534
  accurate. This is not an instance, it's a class, but there is no way to reflect
550
535
  that in :pep:`484` type hints.
@@ -556,12 +541,12 @@ class AbstractJobStore(ABC):
556
541
  self._default_export_file(otherCls, jobStoreFileID, url)
557
542
 
558
543
  def _default_export_file(
559
- self, otherCls: "AbstractJobStore", jobStoreFileID: FileID, url: ParseResult
544
+ self, otherCls: Type["URLAccess"], jobStoreFileID: FileID, url: ParseResult
560
545
  ) -> None:
561
546
  """
562
547
  Refer to exportFile docstring for information about this method.
563
548
 
564
- :param AbstractJobStore otherCls: The concrete subclass of AbstractJobStore that supports
549
+ :param URLAccess otherCls: The class of URLAccess that supports
565
550
  exporting to the given URL. Note that the type annotation here is not completely
566
551
  accurate. This is not an instance, it's a class, but there is no way to reflect
567
552
  that in :pep:`484` type hints.
@@ -576,216 +561,6 @@ class AbstractJobStore(ABC):
576
561
  executable = jobStoreFileID.executable
577
562
  otherCls._write_to_url(readable, url, executable)
578
563
 
579
- @classmethod
580
- def url_exists(cls, src_uri: str) -> bool:
581
- """
582
- Return True if the file at the given URI exists, and False otherwise.
583
-
584
- May raise an error if file existence cannot be determined.
585
-
586
- :param src_uri: URL that points to a file or object in the storage
587
- mechanism of a supported URL scheme e.g. a blob in an AWS s3 bucket.
588
- """
589
- parseResult = urlparse(src_uri)
590
- otherCls = cls._findJobStoreForUrl(parseResult)
591
- return otherCls._url_exists(parseResult)
592
-
593
- @classmethod
594
- def get_size(cls, src_uri: str) -> Optional[int]:
595
- """
596
- Get the size in bytes of the file at the given URL, or None if it cannot be obtained.
597
-
598
- :param src_uri: URL that points to a file or object in the storage
599
- mechanism of a supported URL scheme e.g. a blob in an AWS s3 bucket.
600
- """
601
- parseResult = urlparse(src_uri)
602
- otherCls = cls._findJobStoreForUrl(parseResult)
603
- return otherCls._get_size(parseResult)
604
-
605
- @classmethod
606
- def get_is_directory(cls, src_uri: str) -> bool:
607
- """
608
- Return True if the thing at the given URL is a directory, and False if
609
- it is a file. The URL may or may not end in '/'.
610
- """
611
- parseResult = urlparse(src_uri)
612
- otherCls = cls._findJobStoreForUrl(parseResult)
613
- return otherCls._get_is_directory(parseResult)
614
-
615
- @classmethod
616
- def list_url(cls, src_uri: str) -> list[str]:
617
- """
618
- List the directory at the given URL. Returned path components can be
619
- joined with '/' onto the passed URL to form new URLs. Those that end in
620
- '/' correspond to directories. The provided URL may or may not end with
621
- '/'.
622
-
623
- Currently supported schemes are:
624
-
625
- - 's3' for objects in Amazon S3
626
- e.g. s3://bucket/prefix/
627
-
628
- - 'file' for local files
629
- e.g. file:///local/dir/path/
630
-
631
- :param str src_uri: URL that points to a directory or prefix in the storage mechanism of a
632
- supported URL scheme e.g. a prefix in an AWS s3 bucket.
633
-
634
- :return: A list of URL components in the given directory, already URL-encoded.
635
- """
636
- parseResult = urlparse(src_uri)
637
- otherCls = cls._findJobStoreForUrl(parseResult)
638
- return otherCls._list_url(parseResult)
639
-
640
- @classmethod
641
- def read_from_url(cls, src_uri: str, writable: IO[bytes]) -> tuple[int, bool]:
642
- """
643
- Read the given URL and write its content into the given writable stream.
644
-
645
- Raises FileNotFoundError if the URL doesn't exist.
646
-
647
- :return: The size of the file in bytes and whether the executable permission bit is set
648
- """
649
- parseResult = urlparse(src_uri)
650
- otherCls = cls._findJobStoreForUrl(parseResult)
651
- return otherCls._read_from_url(parseResult, writable)
652
-
653
- @classmethod
654
- def open_url(cls, src_uri: str) -> IO[bytes]:
655
- """
656
- Read from the given URI.
657
-
658
- Raises FileNotFoundError if the URL doesn't exist.
659
-
660
- Has a readable stream interface, unlike :meth:`read_from_url` which
661
- takes a writable stream.
662
- """
663
- parseResult = urlparse(src_uri)
664
- otherCls = cls._findJobStoreForUrl(parseResult)
665
- return otherCls._open_url(parseResult)
666
-
667
- @classmethod
668
- @abstractmethod
669
- def _url_exists(cls, url: ParseResult) -> bool:
670
- """
671
- Return True if the item at the given URL exists, and Flase otherwise.
672
-
673
- May raise an error if file existence cannot be determined.
674
- """
675
- raise NotImplementedError(f"No implementation for {url}")
676
-
677
- @classmethod
678
- @abstractmethod
679
- def _get_size(cls, url: ParseResult) -> Optional[int]:
680
- """
681
- Get the size of the object at the given URL, or None if it cannot be obtained.
682
- """
683
- raise NotImplementedError(f"No implementation for {url}")
684
-
685
- @classmethod
686
- @abstractmethod
687
- def _get_is_directory(cls, url: ParseResult) -> bool:
688
- """
689
- Return True if the thing at the given URL is a directory, and False if
690
- it is a file or it is known not to exist. The URL may or may not end in
691
- '/'.
692
-
693
- :param url: URL that points to a file or object, or directory or prefix,
694
- in the storage mechanism of a supported URL scheme e.g. a blob
695
- in an AWS s3 bucket.
696
- """
697
- raise NotImplementedError(f"No implementation for {url}")
698
-
699
- @classmethod
700
- @abstractmethod
701
- def _read_from_url(cls, url: ParseResult, writable: IO[bytes]) -> tuple[int, bool]:
702
- """
703
- Reads the contents of the object at the specified location and writes it to the given
704
- writable stream.
705
-
706
- Refer to :func:`~AbstractJobStore.importFile` documentation for currently supported URL schemes.
707
-
708
- Raises FileNotFoundError if the thing at the URL is not found.
709
-
710
- :param ParseResult url: URL that points to a file or object in the storage
711
- mechanism of a supported URL scheme e.g. a blob in an AWS s3 bucket.
712
-
713
- :param IO[bytes] writable: a writable stream
714
-
715
- :return: The size of the file in bytes and whether the executable permission bit is set
716
- """
717
- raise NotImplementedError(f"No implementation for {url}")
718
-
719
- @classmethod
720
- @abstractmethod
721
- def _list_url(cls, url: ParseResult) -> list[str]:
722
- """
723
- List the contents of the given URL, which may or may not end in '/'
724
-
725
- Returns a list of URL components. Those that end in '/' are meant to be
726
- directories, while those that do not are meant to be files.
727
-
728
- Refer to :func:`~AbstractJobStore.importFile` documentation for currently supported URL schemes.
729
-
730
- :param ParseResult url: URL that points to a directory or prefix in the
731
- storage mechanism of a supported URL scheme e.g. a prefix in an AWS s3
732
- bucket.
733
-
734
- :return: The children of the given URL, already URL-encoded if
735
- appropriate. (If the URL is a bare path, no encoding is done.)
736
- """
737
- raise NotImplementedError(f"No implementation for {url}")
738
-
739
- @classmethod
740
- @abstractmethod
741
- def _open_url(cls, url: ParseResult) -> IO[bytes]:
742
- """
743
- Get a stream of the object at the specified location.
744
-
745
- Refer to :func:`~AbstractJobStore.importFile` documentation for currently supported URL schemes.
746
-
747
- Raises FileNotFoundError if the thing at the URL is not found.
748
- """
749
- raise NotImplementedError(f"No implementation for {url}")
750
-
751
- @classmethod
752
- @abstractmethod
753
- def _write_to_url(
754
- cls,
755
- readable: Union[IO[bytes], IO[str]],
756
- url: ParseResult,
757
- executable: bool = False,
758
- ) -> None:
759
- """
760
- Reads the contents of the given readable stream and writes it to the object at the
761
- specified location. Raises FileNotFoundError if the URL doesn't exist..
762
-
763
- Refer to AbstractJobStore.importFile documentation for currently supported URL schemes.
764
-
765
- :param Union[IO[bytes], IO[str]] readable: a readable stream
766
-
767
- :param ParseResult url: URL that points to a file or object in the storage
768
- mechanism of a supported URL scheme e.g. a blob in an AWS s3 bucket.
769
-
770
- :param bool executable: determines if the file has executable permissions
771
- """
772
- raise NotImplementedError(f"No implementation for {url}")
773
-
774
- @classmethod
775
- @abstractmethod
776
- def _supports_url(cls, url: ParseResult, export: bool = False) -> bool:
777
- """
778
- Returns True if the job store supports the URL's scheme.
779
-
780
- Refer to AbstractJobStore.importFile documentation for currently supported URL schemes.
781
-
782
- :param ParseResult url: a parsed URL that may be supported
783
-
784
- :param bool export: Determines if the url is supported for exported
785
-
786
- :return bool: returns true if the cls supports the URL
787
- """
788
- raise NotImplementedError(f"No implementation for {url}")
789
564
 
790
565
  @abstractmethod
791
566
  def destroy(self) -> None:
@@ -1686,17 +1461,39 @@ class AbstractJobStore(ABC):
1686
1461
  sharedFileName: str,
1687
1462
  encoding: Optional[str] = None,
1688
1463
  errors: Optional[str] = None,
1689
- ) -> ContextManager[IO[bytes]]:
1464
+ ) -> Union[ContextManager[IO[str]], ContextManager[IO[bytes]]]:
1690
1465
  return self.read_shared_file_stream(sharedFileName, encoding, errors)
1691
1466
 
1467
+ @overload
1692
1468
  @abstractmethod
1693
1469
  @contextmanager
1694
1470
  def read_shared_file_stream(
1695
1471
  self,
1696
1472
  shared_file_name: str,
1697
- encoding: Optional[str] = None,
1473
+ encoding: str,
1474
+ errors: Optional[str] = None,
1475
+ ) -> Iterator[IO[str]]:
1476
+ """If encoding is specified, then a text file handle is provided."""
1477
+
1478
+ @overload
1479
+ @abstractmethod
1480
+ @contextmanager
1481
+ def read_shared_file_stream(
1482
+ self,
1483
+ shared_file_name: str,
1484
+ encoding: Literal[None] = None,
1698
1485
  errors: Optional[str] = None,
1699
1486
  ) -> Iterator[IO[bytes]]:
1487
+ """If no encoding is provided, then a bytest file handle is provided."""
1488
+
1489
+ @abstractmethod
1490
+ @contextmanager
1491
+ def read_shared_file_stream(
1492
+ self,
1493
+ shared_file_name: str,
1494
+ encoding: Optional[str] = None,
1495
+ errors: Optional[str] = None,
1496
+ ) -> Union[Iterator[IO[str]], Iterator[IO[bytes]]]:
1700
1497
  """
1701
1498
  Returns a context manager yielding a readable file handle to the global file referenced
1702
1499
  by the given name.
@@ -1711,7 +1508,6 @@ class AbstractJobStore(ABC):
1711
1508
  are the same as for open(). Defaults to 'strict' when an encoding is specified.
1712
1509
 
1713
1510
  :return: a context manager yielding a readable file handle
1714
- :rtype: Iterator[IO[bytes]]
1715
1511
  """
1716
1512
  raise NotImplementedError()
1717
1513
 
@@ -1851,7 +1647,7 @@ class AbstractJobStore(ABC):
1851
1647
  raise ValueError("Not a valid shared file name: '%s'." % sharedFileName)
1852
1648
 
1853
1649
 
1854
- class JobStoreSupport(AbstractJobStore, metaclass=ABCMeta):
1650
+ class JobStoreSupport(AbstractJobStore, URLAccess, metaclass=ABCMeta):
1855
1651
  """
1856
1652
  A mostly fake JobStore to access URLs not really associated with real job
1857
1653
  stores.
@@ -75,6 +75,7 @@ from toil.lib.io import AtomicFileCreate
75
75
  from toil.lib.memoize import strict_bool
76
76
  from toil.lib.objects import InnerClass
77
77
  from toil.lib.retry import get_error_code, get_error_status, retry
78
+ from toil.lib.url import URLAccess
78
79
 
79
80
  if TYPE_CHECKING:
80
81
  from mypy_boto3_sdb.type_defs import (
@@ -110,7 +111,7 @@ class DomainDoesNotExist(Exception):
110
111
  super().__init__(f"Expected domain {domain_name} to exist!")
111
112
 
112
113
 
113
- class AWSJobStore(AbstractJobStore):
114
+ class AWSJobStore(AbstractJobStore, URLAccess):
114
115
  """
115
116
  A job store that uses Amazon's S3 for file storage and SimpleDB for storing job info and
116
117
  enforcing strong consistency on the S3 file storage. There will be SDB domains for jobs and
@@ -42,11 +42,12 @@ from toil.lib.io import (
42
42
  mkdtemp,
43
43
  robust_rmtree,
44
44
  )
45
+ from toil.lib.url import URLAccess
45
46
 
46
47
  logger = logging.getLogger(__name__)
47
48
 
48
49
 
49
- class FileJobStore(AbstractJobStore):
50
+ class FileJobStore(AbstractJobStore, URLAccess):
50
51
  """
51
52
  A job store that uses a directory on a locally attached file system. To be compatible with
52
53
  distributed batch systems, that file system must be shared by all worker nodes.
@@ -395,6 +396,7 @@ class FileJobStore(AbstractJobStore):
395
396
 
396
397
  @classmethod
397
398
  def _url_exists(cls, url: ParseResult) -> bool:
399
+ # Note that broken symlinks will not be shown to exist.
398
400
  return os.path.exists(cls._extract_path_from_url(url))
399
401
 
400
402
  @classmethod
@@ -771,8 +773,31 @@ class FileJobStore(AbstractJobStore):
771
773
  ) as f:
772
774
  yield f
773
775
 
776
+ @overload
774
777
  @contextmanager
775
- def read_shared_file_stream(self, shared_file_name, encoding=None, errors=None):
778
+ def read_shared_file_stream(
779
+ self,
780
+ shared_file_name: str,
781
+ encoding: str,
782
+ errors: Optional[str] = None,
783
+ ) -> Iterator[IO[str]]: ...
784
+
785
+ @overload
786
+ @contextmanager
787
+ def read_shared_file_stream(
788
+ self,
789
+ shared_file_name: str,
790
+ encoding: Literal[None] = None,
791
+ errors: Optional[str] = None,
792
+ ) -> Iterator[IO[bytes]]: ...
793
+
794
+ @contextmanager
795
+ def read_shared_file_stream(
796
+ self,
797
+ shared_file_name: str,
798
+ encoding: Optional[str] = None,
799
+ errors: Optional[str] = None,
800
+ ) -> Union[Iterator[IO[bytes]], Iterator[IO[str]]]:
776
801
  self._requireValidSharedFileName(shared_file_name)
777
802
  try:
778
803
  with open(