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
@@ -18,18 +18,10 @@ import shutil
18
18
  import time
19
19
  from abc import ABC, abstractmethod
20
20
  from argparse import ArgumentParser, _ArgumentGroup
21
+ from collections.abc import Iterator
21
22
  from contextlib import contextmanager
22
23
  from threading import Condition
23
- from typing import (Any,
24
- ContextManager,
25
- Dict,
26
- Iterator,
27
- List,
28
- NamedTuple,
29
- Optional,
30
- Set,
31
- Union,
32
- cast)
24
+ from typing import Any, ContextManager, NamedTuple, Optional, Union, cast
33
25
 
34
26
  from toil.batchSystems.options import OptionSetter
35
27
  from toil.bus import MessageBus, MessageOutbox
@@ -45,19 +37,41 @@ logger = logging.getLogger(__name__)
45
37
  # Value to use as exitStatus in UpdatedBatchJobInfo.exitStatus when status is not available.
46
38
  EXIT_STATUS_UNAVAILABLE_VALUE = 255
47
39
 
40
+
48
41
  class BatchJobExitReason(enum.IntEnum):
49
- FINISHED: int = 1
42
+ FINISHED = 1
50
43
  """Successfully finished."""
51
- FAILED: int = 2
44
+ FAILED = 2
52
45
  """Job finished, but failed."""
53
- LOST: int = 3
46
+ LOST = 3
54
47
  """Preemptable failure (job's executing host went away)."""
55
- KILLED: int = 4
48
+ KILLED = 4
56
49
  """Job killed before finishing."""
57
- ERROR: int = 5
50
+ ERROR = 5
58
51
  """Internal error."""
59
- MEMLIMIT: int = 6
52
+ MEMLIMIT = 6
60
53
  """Job hit batch system imposed memory limit."""
54
+ MISSING = 7
55
+ """Job disappeared from the scheduler without actually stopping, so Toil killed it."""
56
+ MAXJOBDURATION = 8
57
+ """Job ran longer than --maxJobDuration, so Toil killed it."""
58
+ PARTITION = 9
59
+ """Job was not able to talk to the leader via the job store, so Toil declared it failed."""
60
+
61
+ @classmethod
62
+ def to_string(cls, value: int) -> str:
63
+ """
64
+ Convert to human-readable string.
65
+
66
+ Given an int that may be or may be equal to a value from the enum,
67
+ produce the string value of its matching enum entry, or a stringified
68
+ int.
69
+ """
70
+ try:
71
+ return cls(value).name
72
+ except ValueError:
73
+ return str(value)
74
+
61
75
 
62
76
  class UpdatedBatchJobInfo(NamedTuple):
63
77
  jobID: int
@@ -65,12 +79,14 @@ class UpdatedBatchJobInfo(NamedTuple):
65
79
  """
66
80
  The exit status (integer value) of the job. 0 implies successful.
67
81
 
68
- EXIT_STATUS_UNAVAILABLE_VALUE is used when the exit status is not available (e.g. job is lost).
82
+ EXIT_STATUS_UNAVAILABLE_VALUE is used when the exit status is not available
83
+ (e.g. job is lost, or otherwise died but actual exit code was not reported).
69
84
  """
70
85
 
71
86
  exitReason: Optional[BatchJobExitReason]
72
87
  wallTime: Union[float, int, None]
73
88
 
89
+
74
90
  # Information required for worker cleanup on shutdown of the batch system.
75
91
  class WorkerCleanupInfo(NamedTuple):
76
92
  work_dir: Optional[str]
@@ -88,8 +104,10 @@ class WorkerCleanupInfo(NamedTuple):
88
104
  'onSuccess', 'onError', 'never')
89
105
  """
90
106
 
107
+
91
108
  class AbstractBatchSystem(ABC):
92
109
  """An abstract base class to represent the interface the batch system must provide to Toil."""
110
+
93
111
  @classmethod
94
112
  @abstractmethod
95
113
  def supportsAutoDeployment(cls) -> bool:
@@ -141,22 +159,29 @@ class AbstractBatchSystem(ABC):
141
159
  """
142
160
 
143
161
  @abstractmethod
144
- def issueBatchJob(self, jobDesc: JobDescription, job_environment: Optional[Dict[str, str]] = None) -> int:
162
+ def issueBatchJob(
163
+ self,
164
+ command: str,
165
+ job_desc: JobDescription,
166
+ job_environment: Optional[dict[str, str]] = None,
167
+ ) -> int:
145
168
  """
146
169
  Issues a job with the specified command to the batch system and returns
147
- a unique jobID.
170
+ a unique job ID number.
148
171
 
149
- :param jobDesc: a toil.job.JobDescription
172
+ :param command: the command to execute somewhere to run the Toil
173
+ worker process
174
+ :param job_desc: the JobDescription for the job being run
150
175
  :param job_environment: a collection of job-specific environment
151
- variables to be set on the worker.
176
+ variables to be set on the worker.
152
177
 
153
- :return: a unique jobID that can be used to reference the newly issued
154
- job
178
+ :return: a unique job ID number that can be used to reference the newly
179
+ issued job
155
180
  """
156
181
  raise NotImplementedError()
157
182
 
158
183
  @abstractmethod
159
- def killBatchJobs(self, jobIDs: List[int]) -> None:
184
+ def killBatchJobs(self, jobIDs: list[int]) -> None:
160
185
  """
161
186
  Kills the given job IDs. After returning, the killed jobs will not
162
187
  appear in the results of getRunningBatchJobIDs. The killed job will not
@@ -169,24 +194,24 @@ class AbstractBatchSystem(ABC):
169
194
  # FIXME: Return value should be a set (then also fix the tests)
170
195
 
171
196
  @abstractmethod
172
- def getIssuedBatchJobIDs(self) -> List[int]:
197
+ def getIssuedBatchJobIDs(self) -> list[int]:
173
198
  """
174
199
  Gets all currently issued jobs
175
200
 
176
- :return: A list of jobs (as jobIDs) currently issued (may be running, or may be
177
- waiting to be run). Despite the result being a list, the ordering should not
178
- be depended upon.
201
+ :return: A list of jobs (as job ID numbers) currently issued (may be
202
+ running, or may be waiting to be run). Despite the result being a
203
+ list, the ordering should not be depended upon.
179
204
  """
180
205
  raise NotImplementedError()
181
206
 
182
207
  @abstractmethod
183
- def getRunningBatchJobIDs(self) -> Dict[int, float]:
208
+ def getRunningBatchJobIDs(self) -> dict[int, float]:
184
209
  """
185
- Gets a map of jobs as jobIDs that are currently running (not just waiting)
186
- and how long they have been running, in seconds.
210
+ Gets a map of jobs as job ID numbers that are currently running (not
211
+ just waiting) and how long they have been running, in seconds.
187
212
 
188
- :return: dictionary with currently running jobID keys and how many seconds they have
189
- been running as the value
213
+ :return: dictionary with currently running job ID number keys and how
214
+ many seconds they have been running as the value
190
215
  """
191
216
  raise NotImplementedError()
192
217
 
@@ -268,7 +293,7 @@ class AbstractBatchSystem(ABC):
268
293
  returning nothing, used to update run configuration as a side effect.
269
294
  """
270
295
 
271
- def getWorkerContexts(self) -> List[ContextManager[Any]]:
296
+ def getWorkerContexts(self) -> list[ContextManager[Any]]:
272
297
  """
273
298
  Get a list of picklable context manager objects to wrap worker work in,
274
299
  in order.
@@ -284,7 +309,9 @@ class AbstractBatchSystem(ABC):
284
309
  class BatchSystemSupport(AbstractBatchSystem):
285
310
  """Partial implementation of AbstractBatchSystem, support methods."""
286
311
 
287
- def __init__(self, config: Config, maxCores: float, maxMemory: int, maxDisk: int) -> None:
312
+ def __init__(
313
+ self, config: Config, maxCores: float, maxMemory: int, maxDisk: int
314
+ ) -> None:
288
315
  """
289
316
  Initialize initial state of the object.
290
317
 
@@ -306,7 +333,7 @@ class BatchSystemSupport(AbstractBatchSystem):
306
333
  self.maxCores = maxCores
307
334
  self.maxMemory = maxMemory
308
335
  self.maxDisk = maxDisk
309
- self.environment: Dict[str, str] = {}
336
+ self.environment: dict[str, str] = {}
310
337
  if config.workflowID is None:
311
338
  raise Exception("config.workflowID must be set")
312
339
  else:
@@ -332,9 +359,11 @@ class BatchSystemSupport(AbstractBatchSystem):
332
359
  greater than allowed
333
360
  """
334
361
  try:
335
- for resource, requested, available in [('cores', requirer.cores, self.maxCores),
336
- ('memory', requirer.memory, self.maxMemory),
337
- ('disk', requirer.disk, self.maxDisk)]:
362
+ for resource, requested, available in [
363
+ ("cores", requirer.cores, self.maxCores),
364
+ ("memory", requirer.memory, self.maxMemory),
365
+ ("disk", requirer.disk, self.maxDisk),
366
+ ]:
338
367
  assert requested is not None
339
368
  if requested > available:
340
369
  raise InsufficientSystemResources(requirer, resource, available)
@@ -343,7 +372,7 @@ class BatchSystemSupport(AbstractBatchSystem):
343
372
  except InsufficientSystemResources as e:
344
373
  # Add more annotation info to the error
345
374
  e.batch_system = self.__class__.__name__ or None
346
- e.source = self.config.workDir if e.resource == 'disk' else None
375
+ e.source = self.config.workDir if e.resource == "disk" else None
347
376
  raise e
348
377
 
349
378
  def _check_accelerator_request(self, requirer: Requirer) -> None:
@@ -356,9 +385,12 @@ class BatchSystemSupport(AbstractBatchSystem):
356
385
  """
357
386
  if len(requirer.accelerators) > 0:
358
387
  # By default we assume we can't fulfill any of these
359
- raise InsufficientSystemResources(requirer, 'accelerators', [], details=[
360
- 'The batch system does not support any accelerators.'
361
- ])
388
+ raise InsufficientSystemResources(
389
+ requirer,
390
+ "accelerators",
391
+ [],
392
+ details=["The batch system does not support any accelerators."],
393
+ )
362
394
 
363
395
  def setEnv(self, name: str, value: Optional[str] = None) -> None:
364
396
  """
@@ -415,7 +447,9 @@ class BatchSystemSupport(AbstractBatchSystem):
415
447
  # And if nothing is specified use the workDir.
416
448
  return Toil.getToilWorkDir(self.config.workDir)
417
449
 
418
- def format_std_out_err_path(self, toil_job_id: int, cluster_job_id: str, std: str) -> str:
450
+ def format_std_out_err_path(
451
+ self, toil_job_id: int, cluster_job_id: str, std: str
452
+ ) -> str:
419
453
  """
420
454
  Format path for batch system standard output/error and other files
421
455
  generated by the batch system itself.
@@ -434,18 +468,20 @@ class BatchSystemSupport(AbstractBatchSystem):
434
468
  if self.config.noStdOutErr:
435
469
  return os.devnull
436
470
 
437
- file_name: str = f'toil_{self.config.workflowID}.{toil_job_id}.{cluster_job_id}.{std}.log'
471
+ file_name: str = (
472
+ f"toil_{self.config.workflowID}.{toil_job_id}.{cluster_job_id}.{std}.log"
473
+ )
438
474
  logs_dir: str = self.get_batch_logs_dir()
439
475
  return os.path.join(logs_dir, file_name)
440
-
476
+
441
477
  def format_std_out_err_glob(self, toil_job_id: int) -> str:
442
478
  """
443
479
  Get a glob string that will match all file paths generated by format_std_out_err_path for a job.
444
480
  """
445
- file_glob: str = f'toil_{self.config.workflowID}.{toil_job_id}.*.log'
481
+ file_glob: str = f"toil_{self.config.workflowID}.{toil_job_id}.*.log"
446
482
  logs_dir: str = self.get_batch_logs_dir()
447
483
  return os.path.join(logs_dir, file_glob)
448
-
484
+
449
485
  @staticmethod
450
486
  def workerCleanup(info: WorkerCleanupInfo) -> None:
451
487
  """
@@ -456,23 +492,28 @@ class BatchSystemSupport(AbstractBatchSystem):
456
492
  :param WorkerCleanupInfo info: A named tuple consisting of all the relevant information
457
493
  for cleaning up the worker.
458
494
  """
459
- logger.debug('Attempting worker cleanup')
495
+ logger.debug("Attempting worker cleanup")
460
496
  assert isinstance(info, WorkerCleanupInfo)
461
497
  assert info.workflow_id is not None
462
498
  workflowDir = Toil.getLocalWorkflowDir(info.workflow_id, info.work_dir)
463
- coordination_dir = Toil.get_local_workflow_coordination_dir(info.workflow_id, info.work_dir, info.coordination_dir)
499
+ coordination_dir = Toil.get_local_workflow_coordination_dir(
500
+ info.workflow_id, info.work_dir, info.coordination_dir
501
+ )
464
502
  DeferredFunctionManager.cleanupWorker(coordination_dir)
465
503
  workflowDirContents = os.listdir(workflowDir)
466
- AbstractFileStore.shutdownFileStore(info.workflow_id, info.work_dir, info.coordination_dir)
467
- if info.clean_work_dir in ('always', 'onSuccess', 'onError'):
504
+ AbstractFileStore.shutdownFileStore(
505
+ info.workflow_id, info.work_dir, info.coordination_dir
506
+ )
507
+ if info.clean_work_dir in ("always", "onSuccess", "onError"):
468
508
  if workflowDirContents in ([], [cacheDirName(info.workflow_id)]):
469
- logger.debug('Deleting workflow directory %s', workflowDir)
509
+ logger.debug("Deleting workflow directory %s", workflowDir)
470
510
  shutil.rmtree(workflowDir, ignore_errors=True)
471
511
  if coordination_dir != workflowDir:
472
512
  # No more coordination to do here either.
473
- logger.debug('Deleting coordination directory %s', coordination_dir)
513
+ logger.debug("Deleting coordination directory %s", coordination_dir)
474
514
  shutil.rmtree(coordination_dir, ignore_errors=True)
475
515
 
516
+
476
517
  class NodeInfo:
477
518
  """
478
519
  The coresUsed attribute is a floating point value between 0 (all cores idle) and 1 (all cores
@@ -489,10 +530,17 @@ class NodeInfo:
489
530
  The workers attribute is an integer reflecting the number of workers currently active workers
490
531
  on the node.
491
532
  """
492
- def __init__(self, coresUsed: float, memoryUsed: float,
493
- coresTotal: float, memoryTotal: int,
494
- requestedCores: float, requestedMemory: int,
495
- workers: int) -> None:
533
+
534
+ def __init__(
535
+ self,
536
+ coresUsed: float,
537
+ memoryUsed: float,
538
+ coresTotal: float,
539
+ memoryTotal: int,
540
+ requestedCores: float,
541
+ requestedMemory: int,
542
+ workers: int,
543
+ ) -> None:
496
544
  self.coresUsed = coresUsed
497
545
  self.memoryUsed = memoryUsed
498
546
 
@@ -509,13 +557,15 @@ class AbstractScalableBatchSystem(AbstractBatchSystem):
509
557
  """
510
558
  A batch system that supports a variable number of worker nodes.
511
559
 
512
- Used by :class:`toil.provisioners.clusterScaler.ClusterScaler`
560
+ Used by :class:`toil.provisioners.clusterScaler.ClusterScaler`
513
561
  to scale the number of worker nodes in the cluster
514
562
  up or down depending on overall load.
515
563
  """
516
564
 
517
565
  @abstractmethod
518
- def getNodes(self, preemptible: Optional[bool] = None, timeout: int = 600) -> Dict[str, NodeInfo]:
566
+ def getNodes(
567
+ self, preemptible: Optional[bool] = None, timeout: int = 600
568
+ ) -> dict[str, NodeInfo]:
519
569
  """
520
570
  Returns a dictionary mapping node identifiers of preemptible or non-preemptible nodes to
521
571
  NodeInfo objects, one for each node.
@@ -560,7 +610,15 @@ class AbstractScalableBatchSystem(AbstractBatchSystem):
560
610
 
561
611
 
562
612
  class InsufficientSystemResources(Exception):
563
- def __init__(self, requirer: Requirer, resource: str, available: Optional[ParsedRequirement] = None, batch_system: Optional[str] = None, source: Optional[str] = None, details: List[str] = []) -> None:
613
+ def __init__(
614
+ self,
615
+ requirer: Requirer,
616
+ resource: str,
617
+ available: Optional[ParsedRequirement] = None,
618
+ batch_system: Optional[str] = None,
619
+ source: Optional[str] = None,
620
+ details: list[str] = [],
621
+ ) -> None:
564
622
  """
565
623
  Make a new exception about how we couldn't get enough of something.
566
624
 
@@ -573,7 +631,7 @@ class InsufficientSystemResources(Exception):
573
631
  :param details: Any extra details about the problem that can be attached to the error.
574
632
  """
575
633
 
576
- self.job_name : Optional[str] = str(requirer)
634
+ self.job_name: Optional[str] = str(requirer)
577
635
  self.resource = resource
578
636
  self.requested = cast(ParsedRequirement, getattr(requirer, resource))
579
637
  self.available = available
@@ -586,38 +644,52 @@ class InsufficientSystemResources(Exception):
586
644
  Explain the exception.
587
645
  """
588
646
 
589
- unit = 'bytes of ' if self.resource in ('disk', 'memory') else ''
590
- purpose = ' for temporary space' if self.resource == 'disk' else ''
591
- qualifier = ' free on {self.source}' if self.resource == 'disk' and self.source is not None else ''
647
+ unit = "bytes of " if self.resource in ("disk", "memory") else ""
648
+ purpose = " for temporary space" if self.resource == "disk" else ""
649
+ qualifier = (
650
+ " free on {self.source}"
651
+ if self.resource == "disk" and self.source is not None
652
+ else ""
653
+ )
592
654
 
593
655
  msg = []
594
656
  if self.job_name is not None:
595
- msg.append(f'The job {self.job_name} is requesting ')
657
+ msg.append(f"The job {self.job_name} is requesting ")
596
658
  else:
597
- msg.append(f'Requesting ')
598
- msg.append(f'{self.requested} {unit}{self.resource}')
659
+ msg.append(f"Requesting ")
660
+ msg.append(f"{self.requested} {unit}{self.resource}")
599
661
  msg.append(purpose)
600
662
  if self.available is not None:
601
- msg.append(f', more than the maximum of {self.available} {unit}{self.resource}{qualifier} that {self.batch_system or "this batch system"} was configured with')
602
- if self.resource in ('cores', 'memory', 'disk'):
603
- msg.append(f', or enforced by --max{self.resource.capitalize()}')
663
+ msg.append(
664
+ f', more than the maximum of {self.available} {unit}{self.resource}{qualifier} that {self.batch_system or "this batch system"} was configured with'
665
+ )
666
+ if self.resource in ("cores", "memory", "disk"):
667
+ msg.append(f", or enforced by --max{self.resource.capitalize()}")
604
668
  else:
605
- msg.append(', but that is not available')
606
- msg.append('.')
669
+ msg.append(", but that is not available")
670
+ msg.append(".")
607
671
 
608
- if self.resource == 'disk':
609
- msg.append(' Try setting/changing the toil option "--workDir" or changing the base temporary directory by setting TMPDIR.')
672
+ if self.resource == "disk":
673
+ msg.append(
674
+ ' Try setting/changing the toil option "--workDir" or changing the base temporary directory by setting TMPDIR.'
675
+ )
610
676
 
611
677
  for detail in self.details:
612
- msg.append(' ')
678
+ msg.append(" ")
613
679
  msg.append(detail)
614
680
 
615
- return ''.join(msg)
681
+ return "".join(msg)
616
682
 
617
683
 
618
684
  class AcquisitionTimeoutException(Exception):
619
685
  """To be raised when a resource request times out."""
620
- def __init__(self, resource: str, requested: Union[int, float, Set[int]], available: Union[int, float, Set[int]]) -> None:
686
+
687
+ def __init__(
688
+ self,
689
+ resource: str,
690
+ requested: Union[int, float, set[int]],
691
+ available: Union[int, float, set[int]],
692
+ ) -> None:
621
693
  """
622
694
  Creates an instance of this exception that indicates which resource is insufficient for
623
695
  current demands, as well as the resources requested and actually available.
@@ -637,7 +709,10 @@ class ResourcePool:
637
709
  Provides a context manager to do something with an amount of resource
638
710
  acquired.
639
711
  """
640
- def __init__(self, initial_value: int, resource_type: str, timeout: float = 5) -> None:
712
+
713
+ def __init__(
714
+ self, initial_value: int, resource_type: str, timeout: float = 5
715
+ ) -> None:
641
716
  super().__init__()
642
717
  # We use this condition to signal everyone whenever some resource is released.
643
718
  # We use its associated lock to guard value.
@@ -671,8 +746,11 @@ class ResourcePool:
671
746
  while amount > self.value:
672
747
  if time.time() - startTime >= self.timeout:
673
748
  # This means the thread timed out waiting for the resource.
674
- raise AcquisitionTimeoutException(resource=self.resource_type,
675
- requested=amount, available=self.value)
749
+ raise AcquisitionTimeoutException(
750
+ resource=self.resource_type,
751
+ requested=amount,
752
+ available=self.value,
753
+ )
676
754
  # Allow self.timeout seconds to get the resource, else quit
677
755
  # through the above if condition. This wait + timeout is the
678
756
  # last thing in the loop such that a request that takes longer
@@ -713,7 +791,10 @@ class ResourceSet:
713
791
  Provides a context manager to do something with a set of of resources
714
792
  acquired.
715
793
  """
716
- def __init__(self, initial_value: Set[int], resource_type: str, timeout: float = 5) -> None:
794
+
795
+ def __init__(
796
+ self, initial_value: set[int], resource_type: str, timeout: float = 5
797
+ ) -> None:
717
798
  super().__init__()
718
799
  # We use this condition to signal everyone whenever some resource is released.
719
800
  # We use its associated lock to guard value.
@@ -723,7 +804,7 @@ class ResourceSet:
723
804
  self.resource_type = resource_type
724
805
  self.timeout = timeout
725
806
 
726
- def acquireNow(self, subset: Set[int]) -> bool:
807
+ def acquireNow(self, subset: set[int]) -> bool:
727
808
  """
728
809
  Reserve the given amount of the given resource.
729
810
  Returns True if successful and False if this is not possible immediately.
@@ -735,7 +816,7 @@ class ResourceSet:
735
816
  self.value -= subset
736
817
  return True
737
818
 
738
- def acquire(self, subset: Set[int]) -> None:
819
+ def acquire(self, subset: set[int]) -> None:
739
820
  """
740
821
  Reserve the given amount of the given resource.
741
822
  Raises AcquisitionTimeoutException if this is not possible in under
@@ -746,8 +827,11 @@ class ResourceSet:
746
827
  while subset > self.value:
747
828
  if time.time() - startTime >= self.timeout:
748
829
  # This means the thread timed out waiting for the resource.
749
- raise AcquisitionTimeoutException(resource=self.resource_type,
750
- requested=subset, available=self.value)
830
+ raise AcquisitionTimeoutException(
831
+ resource=self.resource_type,
832
+ requested=subset,
833
+ available=self.value,
834
+ )
751
835
  # Allow self.timeout seconds to get the resource, else quit
752
836
  # through the above if condition. This wait + timeout is the
753
837
  # last thing in the loop such that a request that takes longer
@@ -756,12 +840,12 @@ class ResourceSet:
756
840
  self.condition.wait(timeout=self.timeout)
757
841
  self.value -= subset
758
842
 
759
- def release(self, subset: Set[int]) -> None:
843
+ def release(self, subset: set[int]) -> None:
760
844
  with self.condition:
761
845
  self.value |= subset
762
846
  self.condition.notify_all()
763
847
 
764
- def get_free_snapshot(self) -> Set[int]:
848
+ def get_free_snapshot(self) -> set[int]:
765
849
  """
766
850
  Get a snapshot of what items are free right now.
767
851
  May be stale as soon as you get it, but you will need some kind of hint
@@ -776,7 +860,7 @@ class ResourceSet:
776
860
  return "ResourceSet(%s)" % self.value
777
861
 
778
862
  @contextmanager
779
- def acquisitionOf(self, subset: Set[int]) -> Iterator[None]:
863
+ def acquisitionOf(self, subset: set[int]) -> Iterator[None]:
780
864
  self.acquire(subset)
781
865
  try:
782
866
  yield