toil 6.1.0a1__py3-none-any.whl → 8.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 (193) hide show
  1. toil/__init__.py +122 -315
  2. toil/batchSystems/__init__.py +1 -0
  3. toil/batchSystems/abstractBatchSystem.py +173 -89
  4. toil/batchSystems/abstractGridEngineBatchSystem.py +272 -148
  5. toil/batchSystems/awsBatch.py +244 -135
  6. toil/batchSystems/cleanup_support.py +26 -16
  7. toil/batchSystems/contained_executor.py +31 -28
  8. toil/batchSystems/gridengine.py +86 -50
  9. toil/batchSystems/htcondor.py +166 -89
  10. toil/batchSystems/kubernetes.py +632 -382
  11. toil/batchSystems/local_support.py +20 -15
  12. toil/batchSystems/lsf.py +134 -81
  13. toil/batchSystems/lsfHelper.py +13 -11
  14. toil/batchSystems/mesos/__init__.py +41 -29
  15. toil/batchSystems/mesos/batchSystem.py +290 -151
  16. toil/batchSystems/mesos/executor.py +79 -50
  17. toil/batchSystems/mesos/test/__init__.py +31 -23
  18. toil/batchSystems/options.py +46 -28
  19. toil/batchSystems/registry.py +53 -19
  20. toil/batchSystems/singleMachine.py +296 -125
  21. toil/batchSystems/slurm.py +603 -138
  22. toil/batchSystems/torque.py +47 -33
  23. toil/bus.py +186 -76
  24. toil/common.py +664 -368
  25. toil/cwl/__init__.py +1 -1
  26. toil/cwl/cwltoil.py +1136 -483
  27. toil/cwl/utils.py +17 -22
  28. toil/deferred.py +63 -42
  29. toil/exceptions.py +5 -3
  30. toil/fileStores/__init__.py +5 -5
  31. toil/fileStores/abstractFileStore.py +140 -60
  32. toil/fileStores/cachingFileStore.py +717 -269
  33. toil/fileStores/nonCachingFileStore.py +116 -87
  34. toil/job.py +1225 -368
  35. toil/jobStores/abstractJobStore.py +416 -266
  36. toil/jobStores/aws/jobStore.py +863 -477
  37. toil/jobStores/aws/utils.py +201 -120
  38. toil/jobStores/conftest.py +3 -2
  39. toil/jobStores/fileJobStore.py +292 -154
  40. toil/jobStores/googleJobStore.py +140 -74
  41. toil/jobStores/utils.py +36 -15
  42. toil/leader.py +668 -272
  43. toil/lib/accelerators.py +115 -18
  44. toil/lib/aws/__init__.py +74 -31
  45. toil/lib/aws/ami.py +122 -87
  46. toil/lib/aws/iam.py +284 -108
  47. toil/lib/aws/s3.py +31 -0
  48. toil/lib/aws/session.py +214 -39
  49. toil/lib/aws/utils.py +287 -231
  50. toil/lib/bioio.py +13 -5
  51. toil/lib/compatibility.py +11 -6
  52. toil/lib/conversions.py +104 -47
  53. toil/lib/docker.py +131 -103
  54. toil/lib/ec2.py +361 -199
  55. toil/lib/ec2nodes.py +174 -106
  56. toil/lib/encryption/_dummy.py +5 -3
  57. toil/lib/encryption/_nacl.py +10 -6
  58. toil/lib/encryption/conftest.py +1 -0
  59. toil/lib/exceptions.py +26 -7
  60. toil/lib/expando.py +5 -3
  61. toil/lib/ftp_utils.py +217 -0
  62. toil/lib/generatedEC2Lists.py +127 -19
  63. toil/lib/humanize.py +6 -2
  64. toil/lib/integration.py +341 -0
  65. toil/lib/io.py +141 -15
  66. toil/lib/iterables.py +4 -2
  67. toil/lib/memoize.py +12 -8
  68. toil/lib/misc.py +66 -21
  69. toil/lib/objects.py +2 -2
  70. toil/lib/resources.py +68 -15
  71. toil/lib/retry.py +126 -81
  72. toil/lib/threading.py +299 -82
  73. toil/lib/throttle.py +16 -15
  74. toil/options/common.py +843 -409
  75. toil/options/cwl.py +175 -90
  76. toil/options/runner.py +50 -0
  77. toil/options/wdl.py +73 -17
  78. toil/provisioners/__init__.py +117 -46
  79. toil/provisioners/abstractProvisioner.py +332 -157
  80. toil/provisioners/aws/__init__.py +70 -33
  81. toil/provisioners/aws/awsProvisioner.py +1145 -715
  82. toil/provisioners/clusterScaler.py +541 -279
  83. toil/provisioners/gceProvisioner.py +282 -179
  84. toil/provisioners/node.py +155 -79
  85. toil/realtimeLogger.py +34 -22
  86. toil/resource.py +137 -75
  87. toil/server/app.py +128 -62
  88. toil/server/celery_app.py +3 -1
  89. toil/server/cli/wes_cwl_runner.py +82 -53
  90. toil/server/utils.py +54 -28
  91. toil/server/wes/abstract_backend.py +64 -26
  92. toil/server/wes/amazon_wes_utils.py +21 -15
  93. toil/server/wes/tasks.py +121 -63
  94. toil/server/wes/toil_backend.py +142 -107
  95. toil/server/wsgi_app.py +4 -3
  96. toil/serviceManager.py +58 -22
  97. toil/statsAndLogging.py +224 -70
  98. toil/test/__init__.py +282 -183
  99. toil/test/batchSystems/batchSystemTest.py +460 -210
  100. toil/test/batchSystems/batch_system_plugin_test.py +90 -0
  101. toil/test/batchSystems/test_gridengine.py +173 -0
  102. toil/test/batchSystems/test_lsf_helper.py +67 -58
  103. toil/test/batchSystems/test_slurm.py +110 -49
  104. toil/test/cactus/__init__.py +0 -0
  105. toil/test/cactus/test_cactus_integration.py +56 -0
  106. toil/test/cwl/cwlTest.py +496 -287
  107. toil/test/cwl/measure_default_memory.cwl +12 -0
  108. toil/test/cwl/not_run_required_input.cwl +29 -0
  109. toil/test/cwl/scatter_duplicate_outputs.cwl +40 -0
  110. toil/test/cwl/seqtk_seq.cwl +1 -1
  111. toil/test/docs/scriptsTest.py +69 -46
  112. toil/test/jobStores/jobStoreTest.py +427 -264
  113. toil/test/lib/aws/test_iam.py +118 -50
  114. toil/test/lib/aws/test_s3.py +16 -9
  115. toil/test/lib/aws/test_utils.py +5 -6
  116. toil/test/lib/dockerTest.py +118 -141
  117. toil/test/lib/test_conversions.py +113 -115
  118. toil/test/lib/test_ec2.py +58 -50
  119. toil/test/lib/test_integration.py +104 -0
  120. toil/test/lib/test_misc.py +12 -5
  121. toil/test/mesos/MesosDataStructuresTest.py +23 -10
  122. toil/test/mesos/helloWorld.py +7 -6
  123. toil/test/mesos/stress.py +25 -20
  124. toil/test/options/__init__.py +13 -0
  125. toil/test/options/options.py +42 -0
  126. toil/test/provisioners/aws/awsProvisionerTest.py +320 -150
  127. toil/test/provisioners/clusterScalerTest.py +440 -250
  128. toil/test/provisioners/clusterTest.py +166 -44
  129. toil/test/provisioners/gceProvisionerTest.py +174 -100
  130. toil/test/provisioners/provisionerTest.py +25 -13
  131. toil/test/provisioners/restartScript.py +5 -4
  132. toil/test/server/serverTest.py +188 -141
  133. toil/test/sort/restart_sort.py +137 -68
  134. toil/test/sort/sort.py +134 -66
  135. toil/test/sort/sortTest.py +91 -49
  136. toil/test/src/autoDeploymentTest.py +141 -101
  137. toil/test/src/busTest.py +20 -18
  138. toil/test/src/checkpointTest.py +8 -2
  139. toil/test/src/deferredFunctionTest.py +49 -35
  140. toil/test/src/dockerCheckTest.py +32 -24
  141. toil/test/src/environmentTest.py +135 -0
  142. toil/test/src/fileStoreTest.py +539 -272
  143. toil/test/src/helloWorldTest.py +7 -4
  144. toil/test/src/importExportFileTest.py +61 -31
  145. toil/test/src/jobDescriptionTest.py +46 -21
  146. toil/test/src/jobEncapsulationTest.py +2 -0
  147. toil/test/src/jobFileStoreTest.py +74 -50
  148. toil/test/src/jobServiceTest.py +187 -73
  149. toil/test/src/jobTest.py +121 -71
  150. toil/test/src/miscTests.py +19 -18
  151. toil/test/src/promisedRequirementTest.py +82 -36
  152. toil/test/src/promisesTest.py +7 -6
  153. toil/test/src/realtimeLoggerTest.py +10 -6
  154. toil/test/src/regularLogTest.py +71 -37
  155. toil/test/src/resourceTest.py +80 -49
  156. toil/test/src/restartDAGTest.py +36 -22
  157. toil/test/src/resumabilityTest.py +9 -2
  158. toil/test/src/retainTempDirTest.py +45 -14
  159. toil/test/src/systemTest.py +12 -8
  160. toil/test/src/threadingTest.py +44 -25
  161. toil/test/src/toilContextManagerTest.py +10 -7
  162. toil/test/src/userDefinedJobArgTypeTest.py +8 -5
  163. toil/test/src/workerTest.py +73 -23
  164. toil/test/utils/toilDebugTest.py +103 -33
  165. toil/test/utils/toilKillTest.py +4 -5
  166. toil/test/utils/utilsTest.py +245 -106
  167. toil/test/wdl/wdltoil_test.py +818 -149
  168. toil/test/wdl/wdltoil_test_kubernetes.py +91 -0
  169. toil/toilState.py +120 -35
  170. toil/utils/toilConfig.py +13 -4
  171. toil/utils/toilDebugFile.py +44 -27
  172. toil/utils/toilDebugJob.py +214 -27
  173. toil/utils/toilDestroyCluster.py +11 -6
  174. toil/utils/toilKill.py +8 -3
  175. toil/utils/toilLaunchCluster.py +256 -140
  176. toil/utils/toilMain.py +37 -16
  177. toil/utils/toilRsyncCluster.py +32 -14
  178. toil/utils/toilSshCluster.py +49 -22
  179. toil/utils/toilStats.py +356 -273
  180. toil/utils/toilStatus.py +292 -139
  181. toil/utils/toilUpdateEC2Instances.py +3 -1
  182. toil/version.py +12 -12
  183. toil/wdl/utils.py +5 -5
  184. toil/wdl/wdltoil.py +3913 -1033
  185. toil/worker.py +367 -184
  186. {toil-6.1.0a1.dist-info → toil-8.0.0.dist-info}/LICENSE +25 -0
  187. toil-8.0.0.dist-info/METADATA +173 -0
  188. toil-8.0.0.dist-info/RECORD +253 -0
  189. {toil-6.1.0a1.dist-info → toil-8.0.0.dist-info}/WHEEL +1 -1
  190. toil-6.1.0a1.dist-info/METADATA +0 -125
  191. toil-6.1.0a1.dist-info/RECORD +0 -237
  192. {toil-6.1.0a1.dist-info → toil-8.0.0.dist-info}/entry_points.txt +0 -0
  193. {toil-6.1.0a1.dist-info → toil-8.0.0.dist-info}/top_level.txt +0 -0
@@ -14,7 +14,7 @@
14
14
  import argparse
15
15
  import logging
16
16
  from difflib import get_close_matches
17
- from typing import TYPE_CHECKING, List, Optional, Set, Tuple, Type, Union
17
+ from typing import TYPE_CHECKING, Optional, Union
18
18
 
19
19
  if TYPE_CHECKING:
20
20
  from toil.provisioners.aws.awsProvisioner import AWSProvisioner
@@ -30,8 +30,9 @@ def cluster_factory(
30
30
  clusterType: str = "mesos",
31
31
  zone: Optional[str] = None,
32
32
  nodeStorage: int = 50,
33
- nodeStorageOverrides: Optional[List[str]] = None,
33
+ nodeStorageOverrides: Optional[list[str]] = None,
34
34
  sseKey: Optional[str] = None,
35
+ enable_fuse: bool = False,
35
36
  ) -> Union["AWSProvisioner", "GCEProvisioner"]:
36
37
  """
37
38
  Find and instantiate the appropriate provisioner instance to make clusters in the given cloud.
@@ -45,20 +46,36 @@ def cluster_factory(
45
46
  :param zone: The cloud zone
46
47
  :return: A cluster object for the the cloud type.
47
48
  """
48
- if provisioner == 'aws':
49
+ if provisioner == "aws":
49
50
  try:
50
51
  from toil.provisioners.aws.awsProvisioner import AWSProvisioner
51
52
  except ImportError:
52
- logger.error('The aws extra must be installed to use this provisioner')
53
+ logger.error("The aws extra must be installed to use this provisioner")
53
54
  raise
54
- return AWSProvisioner(clusterName, clusterType, zone, nodeStorage, nodeStorageOverrides, sseKey)
55
- elif provisioner == 'gce':
55
+ return AWSProvisioner(
56
+ clusterName,
57
+ clusterType,
58
+ zone,
59
+ nodeStorage,
60
+ nodeStorageOverrides,
61
+ sseKey,
62
+ enable_fuse,
63
+ )
64
+ elif provisioner == "gce":
56
65
  try:
57
66
  from toil.provisioners.gceProvisioner import GCEProvisioner
58
67
  except ImportError:
59
- logger.error('The google extra must be installed to use this provisioner')
68
+ logger.error("The google extra must be installed to use this provisioner")
60
69
  raise
61
- return GCEProvisioner(clusterName, clusterType, zone, nodeStorage, nodeStorageOverrides, sseKey)
70
+ return GCEProvisioner(
71
+ clusterName,
72
+ clusterType,
73
+ zone,
74
+ nodeStorage,
75
+ nodeStorageOverrides,
76
+ sseKey,
77
+ enable_fuse,
78
+ )
62
79
  else:
63
80
  raise RuntimeError("Invalid provisioner '%s'" % provisioner)
64
81
 
@@ -66,22 +83,39 @@ def cluster_factory(
66
83
  def add_provisioner_options(parser: argparse.ArgumentParser) -> None:
67
84
  group = parser.add_argument_group("Provisioner Options.")
68
85
 
69
- provisioner_choices = ['aws', 'gce']
86
+ provisioner_choices = ["aws", "gce"]
70
87
  # TODO: Better consolidate this provisioner arg and the one in common.py?
71
- group.add_argument('--provisioner', '-p', dest="provisioner", choices=provisioner_choices, default='aws',
72
- help=f"The provisioner for cluster auto-scaling. This is the '--provisioner' option set for "
73
- f"Toil utils like launch-cluster and destroy-cluster, which always require a provisioner, "
74
- f"and so this defaults to: %(default)s. Choices: {provisioner_choices}.")
75
- group.add_argument('-z', '--zone', dest='zone', required=False, default=None,
76
- help="The availability zone of the leader. This parameter can also be set via the 'TOIL_X_ZONE' "
77
- "environment variable, where X is AWS or GCE, or by the ec2_region_name parameter "
78
- "in your .boto file, or derived from the instance metadata if using this utility on an "
79
- "existing EC2 instance.")
80
- group.add_argument("clusterName", help="The name that the cluster will be identifiable by. "
81
- "Must be lowercase and may not contain the '_' character.")
82
-
83
-
84
- def parse_node_types(node_type_specs: Optional[str]) -> List[Tuple[Set[str], Optional[float]]]:
88
+ group.add_argument(
89
+ "--provisioner",
90
+ "-p",
91
+ dest="provisioner",
92
+ choices=provisioner_choices,
93
+ default="aws",
94
+ help=f"The provisioner for cluster auto-scaling. This is the '--provisioner' option set for "
95
+ f"Toil utils like launch-cluster and destroy-cluster, which always require a provisioner, "
96
+ f"and so this defaults to: %(default)s. Choices: {provisioner_choices}.",
97
+ )
98
+ group.add_argument(
99
+ "-z",
100
+ "--zone",
101
+ dest="zone",
102
+ required=False,
103
+ default=None,
104
+ help="The availability zone of the leader. This parameter can also be set via the 'TOIL_X_ZONE' "
105
+ "environment variable, where X is AWS or GCE, or by the ec2_region_name parameter "
106
+ "in your .boto file, or derived from the instance metadata if using this utility on an "
107
+ "existing EC2 instance.",
108
+ )
109
+ group.add_argument(
110
+ "clusterName",
111
+ help="The name that the cluster will be identifiable by. "
112
+ "Must be lowercase and may not contain the '_' character.",
113
+ )
114
+
115
+
116
+ def parse_node_types(
117
+ node_type_specs: Optional[str],
118
+ ) -> list[tuple[set[str], Optional[float]]]:
85
119
  """
86
120
  Parse a specification for zero or more node types.
87
121
 
@@ -106,27 +140,33 @@ def parse_node_types(node_type_specs: Optional[str]) -> List[Tuple[Set[str], Opt
106
140
 
107
141
  if node_type_specs:
108
142
  # Some node types were actually specified
109
- for node_type_spec in node_type_specs.split(','):
143
+ for node_type_spec in node_type_specs.split(","):
110
144
  try:
111
145
  # Types are comma-separated
112
146
  # Then we have the colon and the bid
113
- parts = node_type_spec.split(':')
147
+ parts = node_type_spec.split(":")
114
148
 
115
149
  if len(parts) > 2:
116
150
  # Only one bid allowed
117
- raise ValueError(f'Cound not parse node type "{node_type_spec}": multiple bids')
151
+ raise ValueError(
152
+ f'Cound not parse node type "{node_type_spec}": multiple bids'
153
+ )
118
154
 
119
155
  # Instance types are slash-separated within an equivalence
120
156
  # class
121
- instance_types = set(parts[0].split('/'))
157
+ instance_types = set(parts[0].split("/"))
122
158
 
123
159
  for instance_type in instance_types:
124
- if instance_type == '':
160
+ if instance_type == "":
125
161
  # No empty instance types allowed
126
- raise ValueError(f'Cound not parse node type "{node_type_spec}": empty instance type')
162
+ raise ValueError(
163
+ f'Cound not parse node type "{node_type_spec}": empty instance type'
164
+ )
127
165
 
128
166
  # Build the node type tuple
129
- parsed.append((instance_types, float(parts[1]) if len(parts) > 1 else None))
167
+ parsed.append(
168
+ (instance_types, float(parts[1]) if len(parts) > 1 else None)
169
+ )
130
170
  except Exception as e:
131
171
  if isinstance(e, ValueError):
132
172
  raise
@@ -136,7 +176,9 @@ def parse_node_types(node_type_specs: Optional[str]) -> List[Tuple[Set[str], Opt
136
176
  return parsed
137
177
 
138
178
 
139
- def check_valid_node_types(provisioner, node_types: List[Tuple[Set[str], Optional[float]]]):
179
+ def check_valid_node_types(
180
+ provisioner, node_types: list[tuple[set[str], Optional[float]]]
181
+ ):
140
182
  """
141
183
  Raises if an invalid nodeType is specified for aws or gce.
142
184
 
@@ -147,47 +189,76 @@ def check_valid_node_types(provisioner, node_types: List[Tuple[Set[str], Optiona
147
189
 
148
190
  # check if a valid node type for aws
149
191
  from toil.lib.generatedEC2Lists import E2Instances, regionDict
150
- if provisioner == 'aws':
192
+
193
+ if provisioner == "aws":
151
194
  from toil.lib.aws import get_current_aws_region
152
- current_region = get_current_aws_region() or 'us-west-2'
195
+
196
+ current_region = get_current_aws_region() or "us-west-2"
153
197
  # check if instance type exists in this region
154
198
  for node_type in node_types:
155
199
  for instance_type_name in node_type[0]:
156
200
  if instance_type_name not in regionDict[current_region]:
157
201
  # They probably misspelled it and can't tell.
158
- close = get_close_matches(instance_type_name, regionDict[current_region], 1)
202
+ close = get_close_matches(
203
+ instance_type_name, regionDict[current_region], 1
204
+ )
159
205
  if len(close) > 0:
160
- helpText = ' Did you mean ' + close[0] + '?'
206
+ helpText = " Did you mean " + close[0] + "?"
161
207
  else:
162
- helpText = ''
163
- raise RuntimeError(f'Invalid instance type ({instance_type_name}) specified for AWS in '
164
- f'region: {current_region}.{helpText}')
165
- elif provisioner == 'gce':
208
+ helpText = ""
209
+ raise RuntimeError(
210
+ f"Invalid instance type ({instance_type_name}) specified for AWS in "
211
+ f"region: {current_region}.{helpText}"
212
+ )
213
+ elif provisioner == "gce":
166
214
  for node_type in node_types:
167
215
  for instance_type_name in node_type[0]:
168
216
  if instance_type_name in E2Instances:
169
- raise RuntimeError(f"It looks like you've specified an AWS nodeType with the {provisioner} "
170
- f"provisioner. Please specify a nodeType for {provisioner}.")
217
+ raise RuntimeError(
218
+ f"It looks like you've specified an AWS nodeType with the {provisioner} "
219
+ f"provisioner. Please specify a nodeType for {provisioner}."
220
+ )
171
221
  else:
172
222
  raise RuntimeError(f"Invalid provisioner: {provisioner}")
173
223
 
174
224
 
175
225
  class NoSuchClusterException(Exception):
176
226
  """Indicates that the specified cluster does not exist."""
177
- def __init__(self, cluster_name):
227
+
228
+ def __init__(self, cluster_name: str) -> None:
178
229
  super().__init__(f"The cluster '{cluster_name}' could not be found")
179
230
 
180
231
 
232
+ class NoSuchZoneException(Exception):
233
+ """Indicates that a valid zone could not be found."""
234
+
235
+ def __init__(self) -> None:
236
+ super().__init__(f"No valid zone could be found!")
237
+
238
+
181
239
  class ClusterTypeNotSupportedException(Exception):
182
240
  """Indicates that a provisioner does not support a given cluster type."""
241
+
183
242
  def __init__(self, provisioner_class, cluster_type):
184
- super().__init__(f"The {provisioner_class} provisioner does not support making {cluster_type} clusters")
243
+ super().__init__(
244
+ f"The {provisioner_class} provisioner does not support making {cluster_type} clusters"
245
+ )
246
+
185
247
 
186
248
  class ClusterCombinationNotSupportedException(Exception):
187
249
  """Indicates that a provisioner does not support making a given type of cluster with a given architecture."""
188
- def __init__(self, provisioner_class: Type, cluster_type: str, architecture: str, reason: Optional[str] = None):
189
- message = (f"The {provisioner_class} provisioner does not support making {cluster_type} clusters "
190
- f"using nodes with the {architecture} architecture.")
250
+
251
+ def __init__(
252
+ self,
253
+ provisioner_class: type,
254
+ cluster_type: str,
255
+ architecture: str,
256
+ reason: Optional[str] = None,
257
+ ):
258
+ message = (
259
+ f"The {provisioner_class} provisioner does not support making {cluster_type} clusters "
260
+ f"using nodes with the {architecture} architecture."
261
+ )
191
262
  if reason is not None:
192
263
  message += f" This is because: {reason}"
193
264
  super().__init__(message)