toil 5.11.0__tar.gz → 5.12.0__tar.gz

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 (264) hide show
  1. {toil-5.11.0/src/toil.egg-info → toil-5.12.0}/PKG-INFO +1 -1
  2. toil-5.12.0/requirements-aws.txt +4 -0
  3. {toil-5.11.0 → toil-5.12.0}/requirements-cwl.txt +1 -1
  4. {toil-5.11.0 → toil-5.12.0}/requirements-dev.txt +1 -1
  5. {toil-5.11.0 → toil-5.12.0}/requirements-google.txt +1 -0
  6. {toil-5.11.0 → toil-5.12.0}/requirements-wdl.txt +1 -1
  7. {toil-5.11.0 → toil-5.12.0}/setup.cfg +2 -1
  8. {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/singleMachine.py +1 -1
  9. {toil-5.11.0 → toil-5.12.0}/src/toil/common.py +35 -7
  10. {toil-5.11.0 → toil-5.12.0}/src/toil/cwl/__init__.py +8 -4
  11. {toil-5.11.0 → toil-5.12.0}/src/toil/cwl/cwltoil.py +15 -10
  12. {toil-5.11.0 → toil-5.12.0}/src/toil/fileStores/abstractFileStore.py +8 -1
  13. {toil-5.11.0 → toil-5.12.0}/src/toil/jobStores/abstractJobStore.py +34 -17
  14. {toil-5.11.0 → toil-5.12.0}/src/toil/jobStores/googleJobStore.py +47 -16
  15. {toil-5.11.0 → toil-5.12.0}/src/toil/lib/accelerators.py +35 -3
  16. {toil-5.11.0 → toil-5.12.0}/src/toil/lib/aws/iam.py +54 -13
  17. {toil-5.11.0 → toil-5.12.0}/src/toil/lib/aws/session.py +1 -1
  18. {toil-5.11.0 → toil-5.12.0}/src/toil/lib/docker.py +32 -0
  19. {toil-5.11.0 → toil-5.12.0}/src/toil/server/wsgi_app.py +1 -1
  20. {toil-5.11.0 → toil-5.12.0}/src/toil/test/__init__.py +26 -4
  21. {toil-5.11.0 → toil-5.12.0}/src/toil/test/jobStores/jobStoreTest.py +8 -8
  22. toil-5.12.0/src/toil/test/lib/aws/test_iam.py +139 -0
  23. {toil-5.11.0 → toil-5.12.0}/src/toil/test/provisioners/gceProvisionerTest.py +11 -6
  24. {toil-5.11.0 → toil-5.12.0}/src/toil/test/sort/sortTest.py +6 -3
  25. {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/fileStoreTest.py +5 -3
  26. {toil-5.11.0 → toil-5.12.0}/src/toil/test/wdl/wdltoil_test.py +89 -8
  27. toil-5.12.0/src/toil/version.py +14 -0
  28. {toil-5.11.0 → toil-5.12.0}/src/toil/wdl/wdltoil.py +91 -10
  29. {toil-5.11.0 → toil-5.12.0/src/toil.egg-info}/PKG-INFO +1 -1
  30. {toil-5.11.0 → toil-5.12.0}/src/toil.egg-info/requires.txt +12 -6
  31. toil-5.11.0/requirements-aws.txt +0 -2
  32. toil-5.11.0/src/toil/test/lib/aws/test_iam.py +0 -54
  33. toil-5.11.0/src/toil/version.py +0 -12
  34. {toil-5.11.0 → toil-5.12.0}/LICENSE +0 -0
  35. {toil-5.11.0 → toil-5.12.0}/MANIFEST.in +0 -0
  36. {toil-5.11.0 → toil-5.12.0}/README.rst +0 -0
  37. {toil-5.11.0 → toil-5.12.0}/requirements-encryption.txt +0 -0
  38. {toil-5.11.0 → toil-5.12.0}/requirements-htcondor.txt +0 -0
  39. {toil-5.11.0 → toil-5.12.0}/requirements-kubernetes.txt +0 -0
  40. {toil-5.11.0 → toil-5.12.0}/requirements-mesos.txt +0 -0
  41. {toil-5.11.0 → toil-5.12.0}/requirements-server.txt +0 -0
  42. {toil-5.11.0 → toil-5.12.0}/requirements.txt +0 -0
  43. {toil-5.11.0 → toil-5.12.0}/setup.py +0 -0
  44. {toil-5.11.0 → toil-5.12.0}/src/toil/__init__.py +0 -0
  45. {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/__init__.py +0 -0
  46. {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/abstractBatchSystem.py +0 -0
  47. {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/abstractGridEngineBatchSystem.py +0 -0
  48. {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/awsBatch.py +0 -0
  49. {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/cleanup_support.py +0 -0
  50. {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/contained_executor.py +0 -0
  51. {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/gridengine.py +0 -0
  52. {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/htcondor.py +0 -0
  53. {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/kubernetes.py +0 -0
  54. {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/local_support.py +0 -0
  55. {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/lsf.py +0 -0
  56. {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/lsfHelper.py +0 -0
  57. {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/mesos/__init__.py +0 -0
  58. {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/mesos/batchSystem.py +0 -0
  59. {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/mesos/conftest.py +0 -0
  60. {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/mesos/executor.py +0 -0
  61. {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/mesos/test/__init__.py +0 -0
  62. {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/options.py +0 -0
  63. {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/parasol.py +0 -0
  64. {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/registry.py +0 -0
  65. {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/slurm.py +0 -0
  66. {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/tes.py +0 -0
  67. {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/torque.py +0 -0
  68. {toil-5.11.0 → toil-5.12.0}/src/toil/bus.py +0 -0
  69. {toil-5.11.0 → toil-5.12.0}/src/toil/cwl/conftest.py +0 -0
  70. {toil-5.11.0 → toil-5.12.0}/src/toil/cwl/utils.py +0 -0
  71. {toil-5.11.0 → toil-5.12.0}/src/toil/deferred.py +0 -0
  72. {toil-5.11.0 → toil-5.12.0}/src/toil/exceptions.py +0 -0
  73. {toil-5.11.0 → toil-5.12.0}/src/toil/fileStores/__init__.py +0 -0
  74. {toil-5.11.0 → toil-5.12.0}/src/toil/fileStores/cachingFileStore.py +0 -0
  75. {toil-5.11.0 → toil-5.12.0}/src/toil/fileStores/nonCachingFileStore.py +0 -0
  76. {toil-5.11.0 → toil-5.12.0}/src/toil/job.py +0 -0
  77. {toil-5.11.0 → toil-5.12.0}/src/toil/jobStores/__init__.py +0 -0
  78. {toil-5.11.0 → toil-5.12.0}/src/toil/jobStores/aws/__init__.py +0 -0
  79. {toil-5.11.0 → toil-5.12.0}/src/toil/jobStores/aws/jobStore.py +0 -0
  80. {toil-5.11.0 → toil-5.12.0}/src/toil/jobStores/aws/utils.py +0 -0
  81. {toil-5.11.0 → toil-5.12.0}/src/toil/jobStores/conftest.py +0 -0
  82. {toil-5.11.0 → toil-5.12.0}/src/toil/jobStores/fileJobStore.py +0 -0
  83. {toil-5.11.0 → toil-5.12.0}/src/toil/jobStores/utils.py +0 -0
  84. {toil-5.11.0 → toil-5.12.0}/src/toil/leader.py +0 -0
  85. {toil-5.11.0 → toil-5.12.0}/src/toil/lib/__init__.py +0 -0
  86. {toil-5.11.0 → toil-5.12.0}/src/toil/lib/aws/__init__.py +0 -0
  87. {toil-5.11.0 → toil-5.12.0}/src/toil/lib/aws/ami.py +0 -0
  88. {toil-5.11.0 → toil-5.12.0}/src/toil/lib/aws/utils.py +0 -0
  89. {toil-5.11.0 → toil-5.12.0}/src/toil/lib/bioio.py +0 -0
  90. {toil-5.11.0 → toil-5.12.0}/src/toil/lib/compatibility.py +0 -0
  91. {toil-5.11.0 → toil-5.12.0}/src/toil/lib/conversions.py +0 -0
  92. {toil-5.11.0 → toil-5.12.0}/src/toil/lib/ec2.py +0 -0
  93. {toil-5.11.0 → toil-5.12.0}/src/toil/lib/ec2nodes.py +0 -0
  94. {toil-5.11.0 → toil-5.12.0}/src/toil/lib/encryption/__init__.py +0 -0
  95. {toil-5.11.0 → toil-5.12.0}/src/toil/lib/encryption/_dummy.py +0 -0
  96. {toil-5.11.0 → toil-5.12.0}/src/toil/lib/encryption/_nacl.py +0 -0
  97. {toil-5.11.0 → toil-5.12.0}/src/toil/lib/encryption/conftest.py +0 -0
  98. {toil-5.11.0 → toil-5.12.0}/src/toil/lib/exceptions.py +0 -0
  99. {toil-5.11.0 → toil-5.12.0}/src/toil/lib/expando.py +0 -0
  100. {toil-5.11.0 → toil-5.12.0}/src/toil/lib/generatedEC2Lists.py +0 -0
  101. {toil-5.11.0 → toil-5.12.0}/src/toil/lib/humanize.py +0 -0
  102. {toil-5.11.0 → toil-5.12.0}/src/toil/lib/io.py +0 -0
  103. {toil-5.11.0 → toil-5.12.0}/src/toil/lib/iterables.py +0 -0
  104. {toil-5.11.0 → toil-5.12.0}/src/toil/lib/memoize.py +0 -0
  105. {toil-5.11.0 → toil-5.12.0}/src/toil/lib/misc.py +0 -0
  106. {toil-5.11.0 → toil-5.12.0}/src/toil/lib/objects.py +0 -0
  107. {toil-5.11.0 → toil-5.12.0}/src/toil/lib/resources.py +0 -0
  108. {toil-5.11.0 → toil-5.12.0}/src/toil/lib/retry.py +0 -0
  109. {toil-5.11.0 → toil-5.12.0}/src/toil/lib/threading.py +0 -0
  110. {toil-5.11.0 → toil-5.12.0}/src/toil/lib/throttle.py +0 -0
  111. {toil-5.11.0 → toil-5.12.0}/src/toil/provisioners/__init__.py +0 -0
  112. {toil-5.11.0 → toil-5.12.0}/src/toil/provisioners/abstractProvisioner.py +0 -0
  113. {toil-5.11.0 → toil-5.12.0}/src/toil/provisioners/aws/__init__.py +0 -0
  114. {toil-5.11.0 → toil-5.12.0}/src/toil/provisioners/aws/awsProvisioner.py +0 -0
  115. {toil-5.11.0 → toil-5.12.0}/src/toil/provisioners/clusterScaler.py +0 -0
  116. {toil-5.11.0 → toil-5.12.0}/src/toil/provisioners/gceProvisioner.py +0 -0
  117. {toil-5.11.0 → toil-5.12.0}/src/toil/provisioners/node.py +0 -0
  118. {toil-5.11.0 → toil-5.12.0}/src/toil/realtimeLogger.py +0 -0
  119. {toil-5.11.0 → toil-5.12.0}/src/toil/resource.py +0 -0
  120. {toil-5.11.0 → toil-5.12.0}/src/toil/server/__init__.py +0 -0
  121. {toil-5.11.0 → toil-5.12.0}/src/toil/server/api_spec/__init__.py +0 -0
  122. {toil-5.11.0 → toil-5.12.0}/src/toil/server/api_spec/workflow_execution_service.swagger.yaml +0 -0
  123. {toil-5.11.0 → toil-5.12.0}/src/toil/server/app.py +0 -0
  124. {toil-5.11.0 → toil-5.12.0}/src/toil/server/celery_app.py +0 -0
  125. {toil-5.11.0 → toil-5.12.0}/src/toil/server/cli/__init__.py +0 -0
  126. {toil-5.11.0 → toil-5.12.0}/src/toil/server/cli/wes_cwl_runner.py +0 -0
  127. {toil-5.11.0 → toil-5.12.0}/src/toil/server/utils.py +0 -0
  128. {toil-5.11.0 → toil-5.12.0}/src/toil/server/wes/__init__.py +0 -0
  129. {toil-5.11.0 → toil-5.12.0}/src/toil/server/wes/abstract_backend.py +0 -0
  130. {toil-5.11.0 → toil-5.12.0}/src/toil/server/wes/amazon_wes_utils.py +0 -0
  131. {toil-5.11.0 → toil-5.12.0}/src/toil/server/wes/tasks.py +0 -0
  132. {toil-5.11.0 → toil-5.12.0}/src/toil/server/wes/toil_backend.py +0 -0
  133. {toil-5.11.0 → toil-5.12.0}/src/toil/serviceManager.py +0 -0
  134. {toil-5.11.0 → toil-5.12.0}/src/toil/statsAndLogging.py +0 -0
  135. {toil-5.11.0 → toil-5.12.0}/src/toil/test/batchSystems/__init__.py +0 -0
  136. {toil-5.11.0 → toil-5.12.0}/src/toil/test/batchSystems/batchSystemTest.py +0 -0
  137. {toil-5.11.0 → toil-5.12.0}/src/toil/test/batchSystems/parasolTestSupport.py +0 -0
  138. {toil-5.11.0 → toil-5.12.0}/src/toil/test/batchSystems/test_lsf_helper.py +0 -0
  139. {toil-5.11.0 → toil-5.12.0}/src/toil/test/batchSystems/test_slurm.py +0 -0
  140. {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/__init__.py +0 -0
  141. {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/alwaysfails.cwl +0 -0
  142. {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/colon_test_output.cwl +0 -0
  143. {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/colon_test_output_job.yaml +0 -0
  144. {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/conditional_wf.cwl +0 -0
  145. {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/conditional_wf.yaml +0 -0
  146. {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/conftest.py +0 -0
  147. {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/cwlTest.py +1 -1
  148. {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/directory_from_directory.cwl +0 -0
  149. {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/download.cwl +0 -0
  150. {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/download_directory.cwl +0 -0
  151. {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/download_subdirectory.cwl +0 -0
  152. {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/echo-stderr.cwl +0 -0
  153. {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/echo-stdout-log-dir.cwl +0 -0
  154. {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/echo.cwl +0 -0
  155. {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/echo_string.cwl +0 -0
  156. {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/echo_string_scatter_capture_stdout.cwl +0 -0
  157. {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/file_from_directory.cwl +0 -0
  158. {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/load_contents.cwl +0 -0
  159. {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/mpi_simple.cwl +0 -0
  160. {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/nvidia_smi.cwl +0 -0
  161. {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/revsort.cwl +0 -0
  162. {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/revsort2.cwl +0 -0
  163. {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/revtool.cwl +0 -0
  164. {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/revtool2.cwl +0 -0
  165. {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/s3_secondary_file.cwl +0 -0
  166. {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/seqtk_seq.cwl +0 -0
  167. {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/sorttool.cwl +0 -0
  168. {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/stream.cwl +0 -0
  169. {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/test_filename_conflict_detection.cwl +0 -0
  170. {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/test_filename_conflict_detection_at_root.cwl +0 -0
  171. {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/test_filename_conflict_resolution.cwl +0 -0
  172. {toil-5.11.0 → toil-5.12.0}/src/toil/test/docs/__init__.py +0 -0
  173. {toil-5.11.0 → toil-5.12.0}/src/toil/test/docs/scriptsTest.py +0 -0
  174. {toil-5.11.0 → toil-5.12.0}/src/toil/test/jobStores/__init__.py +0 -0
  175. {toil-5.11.0 → toil-5.12.0}/src/toil/test/lib/__init__.py +0 -0
  176. {toil-5.11.0 → toil-5.12.0}/src/toil/test/lib/aws/__init__.py +0 -0
  177. {toil-5.11.0 → toil-5.12.0}/src/toil/test/lib/aws/test_s3.py +0 -0
  178. {toil-5.11.0 → toil-5.12.0}/src/toil/test/lib/aws/test_utils.py +0 -0
  179. {toil-5.11.0 → toil-5.12.0}/src/toil/test/lib/dockerTest.py +0 -0
  180. {toil-5.11.0 → toil-5.12.0}/src/toil/test/lib/test_conversions.py +0 -0
  181. {toil-5.11.0 → toil-5.12.0}/src/toil/test/lib/test_ec2.py +0 -0
  182. {toil-5.11.0 → toil-5.12.0}/src/toil/test/lib/test_misc.py +0 -0
  183. {toil-5.11.0 → toil-5.12.0}/src/toil/test/mesos/MesosDataStructuresTest.py +0 -0
  184. {toil-5.11.0 → toil-5.12.0}/src/toil/test/mesos/__init__.py +0 -0
  185. {toil-5.11.0 → toil-5.12.0}/src/toil/test/mesos/helloWorld.py +0 -0
  186. {toil-5.11.0 → toil-5.12.0}/src/toil/test/mesos/stress.py +0 -0
  187. {toil-5.11.0 → toil-5.12.0}/src/toil/test/provisioners/__init__.py +0 -0
  188. {toil-5.11.0 → toil-5.12.0}/src/toil/test/provisioners/aws/__init__.py +0 -0
  189. {toil-5.11.0 → toil-5.12.0}/src/toil/test/provisioners/aws/awsProvisionerTest.py +0 -0
  190. {toil-5.11.0 → toil-5.12.0}/src/toil/test/provisioners/clusterScalerTest.py +0 -0
  191. {toil-5.11.0 → toil-5.12.0}/src/toil/test/provisioners/clusterTest.py +0 -0
  192. {toil-5.11.0 → toil-5.12.0}/src/toil/test/provisioners/provisionerTest.py +0 -0
  193. {toil-5.11.0 → toil-5.12.0}/src/toil/test/provisioners/restartScript.py +0 -0
  194. {toil-5.11.0 → toil-5.12.0}/src/toil/test/server/__init__.py +0 -0
  195. {toil-5.11.0 → toil-5.12.0}/src/toil/test/server/serverTest.py +0 -0
  196. {toil-5.11.0 → toil-5.12.0}/src/toil/test/sort/__init__.py +0 -0
  197. {toil-5.11.0 → toil-5.12.0}/src/toil/test/sort/restart_sort.py +0 -0
  198. {toil-5.11.0 → toil-5.12.0}/src/toil/test/sort/sort.py +0 -0
  199. {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/__init__.py +0 -0
  200. {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/autoDeploymentTest.py +0 -0
  201. {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/busTest.py +0 -0
  202. {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/checkpointTest.py +0 -0
  203. {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/deferredFunctionTest.py +0 -0
  204. {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/dockerCheckTest.py +0 -0
  205. {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/helloWorldTest.py +0 -0
  206. {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/importExportFileTest.py +0 -0
  207. {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/jobDescriptionTest.py +0 -0
  208. {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/jobEncapsulationTest.py +0 -0
  209. {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/jobFileStoreTest.py +0 -0
  210. {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/jobServiceTest.py +0 -0
  211. {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/jobTest.py +0 -0
  212. {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/miscTests.py +0 -0
  213. {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/promisedRequirementTest.py +0 -0
  214. {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/promisesTest.py +0 -0
  215. {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/realtimeLoggerTest.py +0 -0
  216. {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/regularLogTest.py +0 -0
  217. {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/resourceTest.py +0 -0
  218. {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/restartDAGTest.py +0 -0
  219. {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/resumabilityTest.py +0 -0
  220. {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/retainTempDirTest.py +0 -0
  221. {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/systemTest.py +0 -0
  222. {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/threadingTest.py +0 -0
  223. {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/toilContextManagerTest.py +0 -0
  224. {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/userDefinedJobArgTypeTest.py +0 -0
  225. {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/workerTest.py +0 -0
  226. {toil-5.11.0 → toil-5.12.0}/src/toil/test/utils/__init__.py +0 -0
  227. {toil-5.11.0 → toil-5.12.0}/src/toil/test/utils/toilDebugTest.py +0 -0
  228. {toil-5.11.0 → toil-5.12.0}/src/toil/test/utils/toilKillTest.py +0 -0
  229. {toil-5.11.0 → toil-5.12.0}/src/toil/test/utils/utilsTest.py +0 -0
  230. {toil-5.11.0 → toil-5.12.0}/src/toil/test/wdl/__init__.py +0 -0
  231. {toil-5.11.0 → toil-5.12.0}/src/toil/test/wdl/builtinTest.py +0 -0
  232. {toil-5.11.0 → toil-5.12.0}/src/toil/test/wdl/conftest.py +0 -0
  233. {toil-5.11.0 → toil-5.12.0}/src/toil/test/wdl/toilwdlTest.py +0 -0
  234. {toil-5.11.0 → toil-5.12.0}/src/toil/toilState.py +0 -0
  235. {toil-5.11.0 → toil-5.12.0}/src/toil/utils/__init__.py +0 -0
  236. {toil-5.11.0 → toil-5.12.0}/src/toil/utils/toilClean.py +0 -0
  237. {toil-5.11.0 → toil-5.12.0}/src/toil/utils/toilDebugFile.py +0 -0
  238. {toil-5.11.0 → toil-5.12.0}/src/toil/utils/toilDebugJob.py +0 -0
  239. {toil-5.11.0 → toil-5.12.0}/src/toil/utils/toilDestroyCluster.py +0 -0
  240. {toil-5.11.0 → toil-5.12.0}/src/toil/utils/toilKill.py +0 -0
  241. {toil-5.11.0 → toil-5.12.0}/src/toil/utils/toilLaunchCluster.py +0 -0
  242. {toil-5.11.0 → toil-5.12.0}/src/toil/utils/toilMain.py +0 -0
  243. {toil-5.11.0 → toil-5.12.0}/src/toil/utils/toilRsyncCluster.py +0 -0
  244. {toil-5.11.0 → toil-5.12.0}/src/toil/utils/toilServer.py +0 -0
  245. {toil-5.11.0 → toil-5.12.0}/src/toil/utils/toilSshCluster.py +0 -0
  246. {toil-5.11.0 → toil-5.12.0}/src/toil/utils/toilStats.py +0 -0
  247. {toil-5.11.0 → toil-5.12.0}/src/toil/utils/toilStatus.py +0 -0
  248. {toil-5.11.0 → toil-5.12.0}/src/toil/utils/toilUpdateEC2Instances.py +0 -0
  249. {toil-5.11.0 → toil-5.12.0}/src/toil/wdl/__init__.py +0 -0
  250. {toil-5.11.0 → toil-5.12.0}/src/toil/wdl/toilwdl.py +0 -0
  251. {toil-5.11.0 → toil-5.12.0}/src/toil/wdl/utils.py +0 -0
  252. {toil-5.11.0 → toil-5.12.0}/src/toil/wdl/versions/__init__.py +0 -0
  253. {toil-5.11.0 → toil-5.12.0}/src/toil/wdl/versions/dev.py +0 -0
  254. {toil-5.11.0 → toil-5.12.0}/src/toil/wdl/versions/draft2.py +0 -0
  255. {toil-5.11.0 → toil-5.12.0}/src/toil/wdl/versions/v1.py +0 -0
  256. {toil-5.11.0 → toil-5.12.0}/src/toil/wdl/wdl_analysis.py +0 -0
  257. {toil-5.11.0 → toil-5.12.0}/src/toil/wdl/wdl_functions.py +0 -0
  258. {toil-5.11.0 → toil-5.12.0}/src/toil/wdl/wdl_synthesis.py +0 -0
  259. {toil-5.11.0 → toil-5.12.0}/src/toil/wdl/wdl_types.py +0 -0
  260. {toil-5.11.0 → toil-5.12.0}/src/toil/worker.py +0 -0
  261. {toil-5.11.0 → toil-5.12.0}/src/toil.egg-info/SOURCES.txt +0 -0
  262. {toil-5.11.0 → toil-5.12.0}/src/toil.egg-info/dependency_links.txt +0 -0
  263. {toil-5.11.0 → toil-5.12.0}/src/toil.egg-info/entry_points.txt +0 -0
  264. {toil-5.11.0 → toil-5.12.0}/src/toil.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: toil
3
- Version: 5.11.0
3
+ Version: 5.12.0
4
4
  Summary: Pipeline management software for clusters.
5
5
  Home-page: https://github.com/DataBiosphere/toil
6
6
  Author: Benedict Paten and the Toil community
@@ -0,0 +1,4 @@
1
+ boto>=2.48.0, <3
2
+ boto3-stubs[s3,sdb,iam,sts,boto3]>=1.28.3.post2, <2
3
+ mypy-boto3-iam>=1.28.3.post2, <2 # Need to force .post1 to be replaced
4
+ moto>=4.1.11, <5
@@ -1,4 +1,4 @@
1
- cwltool==3.1.20230425144158
1
+ cwltool==3.1.20230601100705
2
2
  schema-salad>=8.4.20230128170514,<9
3
3
  galaxy-tool-util
4
4
  ruamel.yaml>=0.15,<=0.17.21
@@ -1,4 +1,4 @@
1
- mock>=4.0.3,<5
1
+ mock>=4.0.3,<6
2
2
  pytest>=6.2.1,<8
3
3
  pytest-cov>=2.12.1,<5
4
4
  pytest-timeout>=1.4.2,<3
@@ -1,2 +1,3 @@
1
1
  apache-libcloud>=2.2.1,<3
2
2
  google-cloud-storage>=2,<=2.8.0
3
+ google-auth>=2.18.1,<3
@@ -1,2 +1,2 @@
1
- miniwdl==1.9.1
1
+ miniwdl==1.10.0
2
2
  wdlparse==0.1.0
@@ -13,7 +13,8 @@ markers =
13
13
  docker_cuda
14
14
  encryption
15
15
  fetchable_appliance
16
- google
16
+ google-project
17
+ google-storage
17
18
  gridengine
18
19
  htcondor
19
20
  integrative
@@ -601,7 +601,7 @@ class SingleMachineBatchSystem(BatchSystemSupport):
601
601
  if job_accelerators:
602
602
  # Try and find some accelerators to use.
603
603
  # Start with all the accelerators that are free right now
604
- accelerator_set : ResourceSet = self.resource_sources[3]
604
+ accelerator_set : ResourceSet = self.resource_sources[-1]
605
605
  snapshot = accelerator_set.get_free_snapshot()
606
606
  # And build a plan of the ones we want
607
607
  accelerators_needed, problem = self._identify_sufficient_accelerators(job_accelerators, snapshot)
@@ -528,7 +528,7 @@ def addOptions(parser: ArgumentParser, config: Optional[Config] = None, jobstore
528
528
  core_options.add_argument("--workDir", dest="workDir", default=None,
529
529
  help="Absolute path to directory where temporary files generated during the Toil "
530
530
  "run should be placed. Standard output and error from batch system jobs "
531
- "(unless --noStdOutErr) will be placed in this directory. A cache directory "
531
+ "(unless --noStdOutErr is set) will be placed in this directory. A cache directory "
532
532
  "may be placed in this directory. Temp files and folders will be placed in a "
533
533
  "directory toil-<workflowID> within workDir. The workflowID is generated by "
534
534
  "Toil and will be reported in the workflow logs. Default is determined by the "
@@ -1266,27 +1266,55 @@ class Toil(ContextManager["Toil"]):
1266
1266
  def import_file(self,
1267
1267
  src_uri: str,
1268
1268
  shared_file_name: str,
1269
- symlink: bool = True) -> None: ...
1269
+ symlink: bool = True,
1270
+ check_existence: bool = True) -> None: ...
1270
1271
 
1271
1272
  @overload
1272
1273
  def import_file(self,
1273
1274
  src_uri: str,
1274
1275
  shared_file_name: None = None,
1275
- symlink: bool = True) -> FileID: ...
1276
+ symlink: bool = True,
1277
+ check_existence: bool = True) -> FileID: ...
1276
1278
 
1277
1279
  def import_file(self,
1278
1280
  src_uri: str,
1279
1281
  shared_file_name: Optional[str] = None,
1280
- symlink: bool = True) -> Optional[FileID]:
1282
+ symlink: bool = True,
1283
+ check_existence: bool = True) -> Optional[FileID]:
1281
1284
  """
1282
1285
  Import the file at the given URL into the job store.
1283
1286
 
1287
+ By default, returns None if the file does not exist.
1288
+
1289
+ :param check_existence: If true, raise FileNotFoundError if the file
1290
+ does not exist. If false, return None when the file does not
1291
+ exist.
1292
+
1284
1293
  See :func:`toil.jobStores.abstractJobStore.AbstractJobStore.importFile` for a
1285
1294
  full description
1286
1295
  """
1287
1296
  self._assertContextManagerUsed()
1288
- src_uri = self.normalize_uri(src_uri, check_existence=True)
1289
- return self._jobStore.import_file(src_uri, shared_file_name=shared_file_name, symlink=symlink)
1297
+ full_uri = self.normalize_uri(src_uri, check_existence=check_existence)
1298
+ try:
1299
+ imported = self._jobStore.import_file(full_uri, shared_file_name=shared_file_name, symlink=symlink)
1300
+ except FileNotFoundError:
1301
+ # TODO: I thought we refactored the different job store import
1302
+ # methods to not raise and instead return None, but that looks to
1303
+ # not currently be the case.
1304
+ if check_existence:
1305
+ raise
1306
+ else:
1307
+ # So translate the raise-based API if needed.
1308
+ # TODO: If check_existence is false but a shared file name is
1309
+ # specified, we have no way to report the lack of file
1310
+ # existence, since we also return None on success!
1311
+ return None
1312
+ if imported is None and shared_file_name is None and check_existence:
1313
+ # We need to protect the caller from missing files.
1314
+ # We think a file was missing, and we got None becasuse of it.
1315
+ # We didn't get None instead because of usign a shared file name.
1316
+ raise FileNotFoundError(f'Could not find file {src_uri}')
1317
+ return imported
1290
1318
 
1291
1319
  @deprecated(new_function_name='export_file')
1292
1320
  def exportFile(self, jobStoreFileID: FileID, dstUrl: str) -> None:
@@ -1308,7 +1336,7 @@ class Toil(ContextManager["Toil"]):
1308
1336
  """
1309
1337
  Given a URI, if it has no scheme, prepend "file:".
1310
1338
 
1311
- :param check_existence: If set, raise an error if a URI points to
1339
+ :param check_existence: If set, raise FileNotFoundError if a URI points to
1312
1340
  a local file that does not exist.
1313
1341
  """
1314
1342
  if urlparse(uri).scheme == 'file':
@@ -15,15 +15,17 @@ import logging
15
15
  from functools import lru_cache
16
16
 
17
17
  from pkg_resources import DistributionNotFound, get_distribution
18
+
18
19
  try:
19
20
  # Setuptools 66+ will raise this if any package on the system has a version that isn't PEP440.
20
21
  # See https://github.com/pypa/setuptools/issues/3772
21
- from setuptools.extern.packaging.version import InvalidVersion # type: ignore
22
+ from setuptools.extern.packaging.version import InvalidVersion # type: ignore
22
23
  except ImportError:
23
24
  # It's not clear that this exception is really part fo the public API, so fake it.
24
- class InvalidVersion(Exception): # type: ignore
25
+ class InvalidVersion(Exception): # type: ignore
25
26
  pass
26
27
 
28
+
27
29
  from toil.version import cwltool_version
28
30
 
29
31
  logger = logging.getLogger(__name__)
@@ -47,8 +49,10 @@ def check_cwltool_version() -> None:
47
49
  except DistributionNotFound:
48
50
  logger.debug("cwltool is not installed.")
49
51
  except InvalidVersion as e:
50
- logger.warning(f"Could not determine the installed version of cwltool because a package "
51
- f"with an unacceptable version is installed: {e}")
52
+ logger.warning(
53
+ f"Could not determine the installed version of cwltool because a package "
54
+ f"with an unacceptable version is installed: {e}"
55
+ )
52
56
 
53
57
 
54
58
  check_cwltool_version()
@@ -60,10 +60,10 @@ import cwl_utils.expression
60
60
  import cwltool.builder
61
61
  import cwltool.command_line_tool
62
62
  import cwltool.context
63
+ import cwltool.cwlprov
63
64
  import cwltool.job
64
65
  import cwltool.load_tool
65
66
  import cwltool.main
66
- import cwltool.provenance
67
67
  import cwltool.resolver
68
68
  import schema_salad.ref_resolver
69
69
  from cwltool.loghandler import _logger as cwllogger
@@ -85,9 +85,9 @@ from cwltool.software_requirements import (
85
85
  )
86
86
  from cwltool.stdfsaccess import StdFsAccess, abspath
87
87
  from cwltool.utils import (
88
- DirectoryType,
89
88
  CWLObjectType,
90
89
  CWLOutputType,
90
+ DirectoryType,
91
91
  adjustDirObjs,
92
92
  aslist,
93
93
  downloadHttpFile,
@@ -110,6 +110,7 @@ from toil.cwl.utils import (
110
110
  download_structure,
111
111
  visit_cwl_class_and_reduce,
112
112
  )
113
+ from toil.exceptions import FailedJobsException
113
114
  from toil.fileStores import FileID
114
115
  from toil.fileStores.abstractFileStore import AbstractFileStore
115
116
  from toil.job import AcceleratorRequirement, Job, Promise, Promised, unwrap
@@ -119,8 +120,6 @@ from toil.jobStores.utils import JobStoreUnavailableException, generate_locator
119
120
  from toil.lib.threading import ExceptionalThread
120
121
  from toil.statsAndLogging import DEFAULT_LOGLEVEL
121
122
  from toil.version import baseVersion
122
- from toil.exceptions import FailedJobsException
123
-
124
123
 
125
124
  logger = logging.getLogger(__name__)
126
125
 
@@ -2015,7 +2014,8 @@ def toilStageFiles(
2015
2014
  f.close()
2016
2015
  # Import it and pack up the file ID so we can turn around and export it.
2017
2016
  file_id_or_contents = (
2018
- "toilfile:" + toil.import_file(f.name, symlink=False).pack()
2017
+ "toilfile:"
2018
+ + toil.import_file(f.name, symlink=False).pack()
2019
2019
  )
2020
2020
 
2021
2021
  if file_id_or_contents.startswith("toildir:"):
@@ -3625,7 +3625,7 @@ def main(args: Optional[List[str]] = None, stdout: TextIO = sys.stdout) -> int:
3625
3625
  loading_context = cwltool.main.setup_loadingContext(None, runtime_context, options)
3626
3626
 
3627
3627
  if options.provenance:
3628
- research_obj = cwltool.provenance.ResearchObject(
3628
+ research_obj = cwltool.cwlprov.ro.ResearchObject(
3629
3629
  temp_prefix_ro=options.tmp_outdir_prefix,
3630
3630
  orcid=options.orcid,
3631
3631
  full_name=options.cwl_full_name,
@@ -3708,8 +3708,9 @@ def main(args: Optional[List[str]] = None, stdout: TextIO = sys.stdout) -> int:
3708
3708
  document_loader = loading_context.loader
3709
3709
 
3710
3710
  if options.provenance and runtime_context.research_obj:
3711
- runtime_context.research_obj.packed_workflow(
3712
- cwltool.main.print_pack(loading_context, uri)
3711
+ cwltool.cwlprov.writablebagfile.packed_workflow(
3712
+ runtime_context.research_obj,
3713
+ cwltool.main.print_pack(loading_context, uri),
3713
3714
  )
3714
3715
 
3715
3716
  try:
@@ -3879,7 +3880,9 @@ def main(args: Optional[List[str]] = None, stdout: TextIO = sys.stdout) -> int:
3879
3880
  toilStageFiles(toil, outobj, outdir, destBucket=options.destBucket)
3880
3881
 
3881
3882
  if runtime_context.research_obj is not None:
3882
- runtime_context.research_obj.create_job(outobj, True)
3883
+ cwltool.cwlprov.writablebagfile.create_job(
3884
+ runtime_context.research_obj, outobj, True
3885
+ )
3883
3886
 
3884
3887
  def remove_at_id(doc: Any) -> None:
3885
3888
  if isinstance(doc, MutableMapping):
@@ -3906,7 +3909,9 @@ def main(args: Optional[List[str]] = None, stdout: TextIO = sys.stdout) -> int:
3906
3909
  workflowobj, document_loader, uri
3907
3910
  )
3908
3911
  runtime_context.research_obj.generate_snapshot(prov_dependencies)
3909
- runtime_context.research_obj.close(options.provenance)
3912
+ cwltool.cwlprov.writablebagfile.close_ro(
3913
+ runtime_context.research_obj, options.provenance
3914
+ )
3910
3915
 
3911
3916
  if not options.destBucket and options.compute_checksum:
3912
3917
  visit_class(
@@ -346,7 +346,14 @@ class AbstractFileStore(ABC):
346
346
  # For each access record
347
347
  if len(item) == 2:
348
348
  # If it has a name, dump wit the name
349
- logger.warning('Downloaded file \'%s\' to path \'%s\'', *item)
349
+ file_id, dest_path = item
350
+ if os.path.exists(dest_path):
351
+ if os.path.islink(dest_path):
352
+ logger.warning('Symlinked file \'%s\' to path \'%s\'', file_id, dest_path)
353
+ else:
354
+ logger.warning('Downloaded file \'%s\' to path \'%s\'', file_id, dest_path)
355
+ else:
356
+ logger.warning('Downloaded file \'%s\' to path \'%s\' (gone!)', file_id, dest_path)
350
357
  else:
351
358
  # Otherwise dump without the name
352
359
  logger.warning('Streamed file \'%s\'', *item)
@@ -43,11 +43,10 @@ else:
43
43
  from typing_extensions import Literal
44
44
 
45
45
  from urllib.parse import ParseResult, urlparse
46
+ from urllib.error import HTTPError
46
47
  from urllib.request import urlopen
47
48
  from uuid import uuid4
48
49
 
49
- from requests.exceptions import HTTPError
50
-
51
50
  from toil.common import Config, getNodeID, safeUnpickleFromStream
52
51
  from toil.fileStores import FileID
53
52
  from toil.job import (CheckpointJobDescription,
@@ -420,6 +419,8 @@ class AbstractJobStore(ABC):
420
419
  - 'gs'
421
420
  e.g. gs://bucket/file
422
421
 
422
+ Raises FileNotFoundError if the file does not exist.
423
+
423
424
  :param str src_uri: URL that points to a file or object in the storage mechanism of a
424
425
  supported URL scheme e.g. a blob in an AWS s3 bucket. It must be a file, not a
425
426
  directory or prefix.
@@ -453,6 +454,8 @@ class AbstractJobStore(ABC):
453
454
  asks the other job store class for a stream and writes that stream as either a regular or
454
455
  a shared file.
455
456
 
457
+ Raises FileNotFoundError if the file does not exist.
458
+
456
459
  :param AbstractJobStore otherCls: The concrete subclass of AbstractJobStore that supports
457
460
  reading from the given URL and getting the file size from the URL.
458
461
 
@@ -587,6 +590,8 @@ class AbstractJobStore(ABC):
587
590
  """
588
591
  Read the given URL and write its content into the given writable stream.
589
592
 
593
+ Raises FileNotFoundError if the URL doesn't exist.
594
+
590
595
  :return: The size of the file in bytes and whether the executable permission bit is set
591
596
  :rtype: Tuple[int, bool]
592
597
  """
@@ -618,8 +623,12 @@ class AbstractJobStore(ABC):
618
623
  Reads the contents of the object at the specified location and writes it to the given
619
624
  writable stream.
620
625
 
626
+ Raises FileNotFoundError if the URL doesn't exist.
627
+
621
628
  Refer to :func:`~AbstractJobStore.importFile` documentation for currently supported URL schemes.
622
629
 
630
+ Raises FileNotFoundError if the thing at the URL is not found.
631
+
623
632
  :param ParseResult url: URL that points to a file or object in the storage
624
633
  mechanism of a supported URL scheme e.g. a blob in an AWS s3 bucket.
625
634
 
@@ -635,7 +644,7 @@ class AbstractJobStore(ABC):
635
644
  def _write_to_url(cls, readable: Union[IO[bytes], IO[str]], url: ParseResult, executable: bool = False) -> None:
636
645
  """
637
646
  Reads the contents of the given readable stream and writes it to the object at the
638
- specified location.
647
+ specified location. Raises FileNotFoundError if the URL doesn't exist..
639
648
 
640
649
  Refer to AbstractJobStore.importFile documentation for currently supported URL schemes.
641
650
 
@@ -1707,20 +1716,28 @@ class JobStoreSupport(AbstractJobStore, metaclass=ABCMeta):
1707
1716
  # We can only retry on errors that happen as responses to the request.
1708
1717
  # If we start getting file data, and the connection drops, we fail.
1709
1718
  # So we don't have to worry about writing the start of the file twice.
1710
- with closing(urlopen(url.geturl())) as readable:
1711
- # Make something to count the bytes we get
1712
- # We need to put the actual count in a container so our
1713
- # nested function can modify it without creating its own
1714
- # local with the same name.
1715
- size = [0]
1716
- def count(l: int) -> None:
1717
- size[0] += l
1718
- counter = WriteWatchingStream(writable)
1719
- counter.onWrite(count)
1720
-
1721
- # Do the download
1722
- shutil.copyfileobj(readable, counter)
1723
- return size[0], False
1719
+ try:
1720
+ with closing(urlopen(url.geturl())) as readable:
1721
+ # Make something to count the bytes we get
1722
+ # We need to put the actual count in a container so our
1723
+ # nested function can modify it without creating its own
1724
+ # local with the same name.
1725
+ size = [0]
1726
+ def count(l: int) -> None:
1727
+ size[0] += l
1728
+ counter = WriteWatchingStream(writable)
1729
+ counter.onWrite(count)
1730
+
1731
+ # Do the download
1732
+ shutil.copyfileobj(readable, counter)
1733
+ return size[0], False
1734
+ except HTTPError as e:
1735
+ if e.code == 404:
1736
+ # Translate into a FileNotFoundError for detecting
1737
+ # un-importable files
1738
+ raise FileNotFoundError(str(url)) from e
1739
+ else:
1740
+ raise
1724
1741
 
1725
1742
  @classmethod
1726
1743
  def _get_is_directory(cls, url: ParseResult) -> bool:
@@ -27,6 +27,7 @@ from google.api_core.exceptions import (GoogleAPICallError,
27
27
  InternalServerError,
28
28
  ServiceUnavailable)
29
29
  from google.cloud import exceptions, storage
30
+ from google.auth.exceptions import DefaultCredentialsError
30
31
 
31
32
  from toil.jobStores.abstractJobStore import (AbstractJobStore,
32
33
  JobStoreExistsException,
@@ -112,25 +113,54 @@ class GoogleJobStore(AbstractJobStore):
112
113
  self.readStatsBaseID = self.statsReadPrefix+self.statsBaseID
113
114
 
114
115
  self.sseKey = None
116
+ self.storageClient = self.create_client()
115
117
 
116
- # Determine if we have an override environment variable for our credentials.
117
- # We don't pull out the filename; we just see if a name is there.
118
- self.credentialsFromEnvironment = bool(os.getenv('GOOGLE_APPLICATION_CREDENTIALS', False))
119
118
 
120
- if self.credentialsFromEnvironment and not os.path.exists(os.getenv('GOOGLE_APPLICATION_CREDENTIALS')):
119
+ @classmethod
120
+ def create_client(cls) -> storage.Client:
121
+ """
122
+ Produce a client for Google Sotrage with the highest level of access we can get.
123
+
124
+ Fall back to anonymous access if no project is available, unlike the
125
+ Google Storage module's behavior.
126
+
127
+ Warn if GOOGLE_APPLICATION_CREDENTIALS is set but not actually present.
128
+ """
129
+
130
+ # Determine if we have an override environment variable for our credentials.
131
+ # We get the path to check existence, but Google Storage works out what
132
+ # to use later by looking at the environment again.
133
+ credentials_path: Optional[str] = os.getenv('GOOGLE_APPLICATION_CREDENTIALS', None)
134
+ if credentials_path is not None and not os.path.exists(credentials_path):
121
135
  # If the file is missing, complain.
122
136
  # This variable holds a file name and not any sensitive data itself.
123
137
  log.warning("File '%s' from GOOGLE_APPLICATION_CREDENTIALS is unavailable! "
124
138
  "We may not be able to authenticate!",
125
- os.getenv('GOOGLE_APPLICATION_CREDENTIALS'))
126
-
127
- if not self.credentialsFromEnvironment and os.path.exists(self.nodeServiceAccountJson):
128
- # load credentials from a particular file on GCE nodes if an override path is not set
129
- self.storageClient = storage.Client.from_service_account_json(self.nodeServiceAccountJson)
130
- else:
131
- # Either a filename is specified, or our fallback file isn't there.
139
+ credentials_path)
140
+
141
+ if credentials_path is None and os.path.exists(cls.nodeServiceAccountJson):
142
+ try:
143
+ # load credentials from a particular file on GCE nodes if an override path is not set
144
+ return storage.Client.from_service_account_json(cls.nodeServiceAccountJson)
145
+ except OSError:
146
+ # Probably we don't have permission to use the file.
147
+ log.warning("File '%s' exists but didn't work to authenticate!",
148
+ cls.nodeServiceAccountJson)
149
+ pass
150
+
151
+ # Either a filename is specified, or our fallback file isn't there.
152
+ try:
132
153
  # See if Google can work out how to authenticate.
133
- self.storageClient = storage.Client()
154
+ return storage.Client()
155
+ except (DefaultCredentialsError, EnvironmentError):
156
+ # Depending on which Google codepath or module version (???)
157
+ # realizes we have no credentials, we can get an EnvironemntError,
158
+ # or the new DefaultCredentialsError we are supposedly specced to
159
+ # get.
160
+
161
+ # Google can't find credentials, fall back to being anonymous.
162
+ # This is likely to happen all the time so don't warn.
163
+ return storage.Client.create_anonymous_client()
134
164
 
135
165
 
136
166
  @google_retry
@@ -244,10 +274,11 @@ class GoogleJobStore(AbstractJobStore):
244
274
 
245
275
  env = {}
246
276
 
247
- if self.credentialsFromEnvironment:
277
+ credentials_path: Optional[str] = os.getenv('GOOGLE_APPLICATION_CREDENTIALS', None)
278
+ if credentials_path is not None:
248
279
  # Send along the environment variable that points to the credentials file.
249
280
  # It must be available in the same place on all nodes.
250
- env['GOOGLE_APPLICATION_CREDENTIALS'] = os.getenv('GOOGLE_APPLICATION_CREDENTIALS')
281
+ env['GOOGLE_APPLICATION_CREDENTIALS'] = credentials_path
251
282
 
252
283
  return env
253
284
 
@@ -351,8 +382,8 @@ class GoogleJobStore(AbstractJobStore):
351
382
  if fileName.startswith('/'):
352
383
  fileName = fileName[1:]
353
384
 
354
- storageClient = storage.Client()
355
- bucket = storageClient.get_bucket(bucketName)
385
+ storageClient = cls.create_client()
386
+ bucket = storageClient.bucket(bucket_name=bucketName)
356
387
  blob = bucket.blob(compat_bytes(fileName))
357
388
 
358
389
  if exists:
@@ -14,8 +14,9 @@
14
14
 
15
15
  """Accelerator (i.e. GPU) utilities for Toil"""
16
16
 
17
+ import os
17
18
  import subprocess
18
- from typing import Dict, List, Optional, Set
19
+ from typing import Dict, List, Optional, Set, Union
19
20
  from xml.dom import minidom
20
21
 
21
22
  from toil.job import AcceleratorRequirement
@@ -37,6 +38,37 @@ def have_working_nvidia_smi() -> bool:
37
38
  return False
38
39
  return True
39
40
 
41
+ @memoize
42
+ def get_host_accelerator_numbers() -> List[int]:
43
+ """
44
+ Work out what accelerator is what.
45
+
46
+ For each accelerator visible to us, returns the host-side (for example,
47
+ outside-of-Slurm-job) number for that accelerator. It is often the same as
48
+ the apparent number.
49
+
50
+ Can be used with Docker's --gpus='"device=#,#,#"' option to forward the
51
+ right GPUs as seen from a Docker daemon.
52
+ """
53
+
54
+ for number_list_var in ['SLURM_STEP_GPUS', 'SLURM_JOB_GPUS', 'CUDA_VISIBLE_DEVICES', 'NVIDIA_VISIBLE_DEVICES']:
55
+ # Any of these can have a list of GPU numbers, but the CUDA/NVIDIA ones
56
+ # also support a system of GPU GUIDs that we don't support.
57
+ # TODO: If Slurm confinement is set we ignore any attempt to further
58
+ # limit us with the app-level variables. Does that make sense? Writing
59
+ # code to translate through would be hard and probably not actually
60
+ # useful.
61
+ if number_list_var in os.environ:
62
+ device_string = os.environ[number_list_var]
63
+ # Parse all the numbers we have
64
+ device_numbers = [int(part) for part in device_string.split(',') if part.isnumeric()]
65
+ if len(device_numbers) > 0:
66
+ # We found some numbers, so use those
67
+ return device_numbers
68
+
69
+ # If we don't see a set of limits we understand, say we have all nvidia GPUs
70
+ return list(range(count_nvidia_gpus()))
71
+
40
72
  @memoize
41
73
  def have_working_nvidia_docker_runtime() -> bool:
42
74
  """
@@ -45,7 +77,7 @@ def have_working_nvidia_docker_runtime() -> bool:
45
77
  try:
46
78
  # The runtime injects nvidia-smi; it doesn't seem to have to be in the image we use here
47
79
  subprocess.check_call(['docker', 'run', '--rm', '--runtime', 'nvidia', '--gpus', 'all', 'ubuntu:20.04', 'nvidia-smi'])
48
- except subprocess.CalledProcessError:
80
+ except (FileNotFoundError, subprocess.CalledProcessError):
49
81
  return False
50
82
  return True
51
83
 
@@ -83,7 +115,7 @@ def get_individual_local_accelerators() -> List[AcceleratorRequirement]:
83
115
  # For now we only know abput nvidia GPUs
84
116
  return [{'kind': 'gpu', 'brand': 'nvidia', 'api': 'cuda', 'count': 1} for _ in range(count_nvidia_gpus())]
85
117
 
86
- def get_restrictive_environment_for_local_accelerators(accelerator_numbers : Set[int]) -> Dict[str, str]:
118
+ def get_restrictive_environment_for_local_accelerators(accelerator_numbers : Union[Set[int], List[int]]) -> Dict[str, str]:
87
119
  """
88
120
  Get environment variables which can be applied to a process to restrict it
89
121
  to using only the given accelerator numbers.
@@ -3,7 +3,7 @@ import json
3
3
  import logging
4
4
  from collections import defaultdict
5
5
  from functools import lru_cache
6
- from typing import Any, Dict, List, Optional, Set, cast
6
+ from typing import Any, Dict, List, Optional, Set, cast, Union, Sequence
7
7
 
8
8
  import boto3
9
9
  from mypy_boto3_iam import IAMClient
@@ -80,8 +80,6 @@ def add_to_action_collection(a: AllowedActionCollection, b: AllowedActionCollect
80
80
  return to_return
81
81
 
82
82
 
83
-
84
-
85
83
  def policy_permissions_allow(given_permissions: AllowedActionCollection, required_permissions: List[str] = []) -> bool:
86
84
  """
87
85
  Check whether given set of actions are a subset of another given set of actions, returns true if they are
@@ -189,7 +187,23 @@ def allowed_actions_roles(iam: IAMClient, policy_names: List[str], role_name: st
189
187
 
190
188
  return allowed_actions
191
189
 
192
- def allowed_actions_users(iam: IAMClient, policy_names: List[str], user_name: str) -> AllowedActionCollection:
190
+
191
+ def collect_policy_actions(policy_documents: Sequence[Union[str, Dict[str, Any]]]) -> AllowedActionCollection:
192
+ """
193
+ Collect all of the actions allowed by the given policy documents into one AllowedActionCollection.
194
+ """
195
+ allowed_actions: AllowedActionCollection = init_action_collection()
196
+ for policy_str in policy_documents:
197
+ # sometimes a string is returned from the api, so convert to a dictionary
198
+ if isinstance(policy_str, dict):
199
+ policy_dict = policy_str
200
+ else:
201
+ policy_dict = json.loads(policy_str)
202
+ allowed_actions = add_to_action_collection(allowed_actions, get_actions_from_policy_document(policy_dict))
203
+ return allowed_actions
204
+
205
+
206
+ def allowed_actions_user(iam: IAMClient, policy_names: List[str], user_name: str) -> AllowedActionCollection:
193
207
  """
194
208
  Gets all allowed actions for a user given by user_name, returns a dictionary, keyed by resource,
195
209
  with a list of permissions allowed for each given resource.
@@ -198,17 +212,34 @@ def allowed_actions_users(iam: IAMClient, policy_names: List[str], user_name: st
198
212
  :param policy_names: Name of policy document associated with a user
199
213
  :param user_name: Name of user to get associated policies
200
214
  """
201
- allowed_actions: AllowedActionCollection = init_action_collection()
202
-
203
- for policy_name in policy_names:
204
- user_policy = iam.get_user_policy(
215
+ user_policies = [
216
+ iam.get_user_policy(
205
217
  UserName=user_name,
206
218
  PolicyName=policy_name
207
- )
208
- policy_document = json.loads(user_policy["PolicyDocument"])
209
- allowed_actions = add_to_action_collection(allowed_actions, get_actions_from_policy_document(policy_document))
219
+ )["PolicyDocument"]
220
+ for policy_name in policy_names
221
+ ]
222
+ return collect_policy_actions(user_policies)
223
+
224
+
225
+ def allowed_actions_group(iam: IAMClient, policy_names: List[str], group_name: str) -> AllowedActionCollection:
226
+ """
227
+ Gets all allowed actions for a group given by group_name, returns a dictionary, keyed by resource,
228
+ with a list of permissions allowed for each given resource.
229
+
230
+ :param iam: IAM client to use
231
+ :param policy_names: Name of policy document associated with a user
232
+ :param group_name: Name of group to get associated policies
233
+ """
234
+ group_policies = [
235
+ iam.get_group_policy(
236
+ GroupName=group_name,
237
+ PolicyName=policy_name
238
+ )["PolicyDocument"]
239
+ for policy_name in policy_names
240
+ ]
241
+ return collect_policy_actions(group_policies)
210
242
 
211
- return allowed_actions
212
243
 
213
244
  def get_policy_permissions(region: str) -> AllowedActionCollection:
214
245
  """
@@ -229,9 +260,19 @@ def get_policy_permissions(region: str) -> AllowedActionCollection:
229
260
  attached_policies = iam.list_attached_user_policies(UserName=user['User']['UserName'])
230
261
  user_attached_policies = allowed_actions_attached(iam, attached_policies['AttachedPolicies'])
231
262
  allowed_actions = add_to_action_collection(allowed_actions, user_attached_policies)
232
- user_inline_policies = allowed_actions_users(iam, list_policies['PolicyNames'], user['User']['UserName'])
263
+ user_inline_policies = allowed_actions_user(iam, list_policies['PolicyNames'], user['User']['UserName'])
233
264
  allowed_actions = add_to_action_collection(allowed_actions, user_inline_policies)
234
265
 
266
+ # grab group policies associated with the user
267
+ groups = iam.list_groups_for_user(UserName=user['User']['UserName'])
268
+ for group in groups["Groups"]:
269
+ list_policies = iam.list_group_policies(GroupName=group['GroupName'])
270
+ attached_policies = iam.list_attached_group_policies(GroupName=group['GroupName'])
271
+ group_attached_policies = allowed_actions_attached(iam, attached_policies['AttachedPolicies'])
272
+ allowed_actions = add_to_action_collection(allowed_actions, group_attached_policies)
273
+ group_inline_policies = allowed_actions_group(iam, list_policies['PolicyNames'], group['GroupName'])
274
+ allowed_actions = add_to_action_collection(allowed_actions, group_inline_policies)
275
+
235
276
  except:
236
277
  # If not successful, we check the role associated with an instance profile
237
278
  # and grab the role's associated permissions