toil 9.1.1__py3-none-any.whl → 9.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. toil/__init__.py +5 -9
  2. toil/batchSystems/abstractBatchSystem.py +23 -22
  3. toil/batchSystems/abstractGridEngineBatchSystem.py +17 -12
  4. toil/batchSystems/awsBatch.py +8 -8
  5. toil/batchSystems/cleanup_support.py +4 -4
  6. toil/batchSystems/contained_executor.py +3 -3
  7. toil/batchSystems/gridengine.py +3 -4
  8. toil/batchSystems/htcondor.py +5 -5
  9. toil/batchSystems/kubernetes.py +65 -63
  10. toil/batchSystems/local_support.py +2 -3
  11. toil/batchSystems/lsf.py +6 -7
  12. toil/batchSystems/mesos/batchSystem.py +11 -7
  13. toil/batchSystems/mesos/test/__init__.py +1 -2
  14. toil/batchSystems/options.py +9 -10
  15. toil/batchSystems/registry.py +3 -7
  16. toil/batchSystems/singleMachine.py +8 -11
  17. toil/batchSystems/slurm.py +49 -38
  18. toil/batchSystems/torque.py +3 -4
  19. toil/bus.py +36 -34
  20. toil/common.py +129 -89
  21. toil/cwl/cwltoil.py +857 -729
  22. toil/cwl/utils.py +44 -35
  23. toil/fileStores/__init__.py +3 -1
  24. toil/fileStores/abstractFileStore.py +28 -30
  25. toil/fileStores/cachingFileStore.py +8 -8
  26. toil/fileStores/nonCachingFileStore.py +10 -21
  27. toil/job.py +159 -158
  28. toil/jobStores/abstractJobStore.py +68 -69
  29. toil/jobStores/aws/jobStore.py +249 -213
  30. toil/jobStores/aws/utils.py +13 -24
  31. toil/jobStores/fileJobStore.py +28 -22
  32. toil/jobStores/googleJobStore.py +21 -17
  33. toil/jobStores/utils.py +3 -7
  34. toil/leader.py +17 -22
  35. toil/lib/accelerators.py +6 -4
  36. toil/lib/aws/__init__.py +9 -10
  37. toil/lib/aws/ami.py +33 -19
  38. toil/lib/aws/iam.py +6 -6
  39. toil/lib/aws/s3.py +259 -157
  40. toil/lib/aws/session.py +76 -76
  41. toil/lib/aws/utils.py +51 -43
  42. toil/lib/checksum.py +19 -15
  43. toil/lib/compatibility.py +3 -2
  44. toil/lib/conversions.py +45 -18
  45. toil/lib/directory.py +29 -26
  46. toil/lib/docker.py +93 -99
  47. toil/lib/dockstore.py +77 -50
  48. toil/lib/ec2.py +39 -38
  49. toil/lib/ec2nodes.py +11 -4
  50. toil/lib/exceptions.py +8 -5
  51. toil/lib/ftp_utils.py +9 -14
  52. toil/lib/generatedEC2Lists.py +161 -20
  53. toil/lib/history.py +141 -97
  54. toil/lib/history_submission.py +163 -72
  55. toil/lib/io.py +27 -17
  56. toil/lib/memoize.py +2 -1
  57. toil/lib/misc.py +15 -11
  58. toil/lib/pipes.py +40 -25
  59. toil/lib/plugins.py +12 -8
  60. toil/lib/resources.py +1 -0
  61. toil/lib/retry.py +32 -38
  62. toil/lib/threading.py +12 -12
  63. toil/lib/throttle.py +1 -2
  64. toil/lib/trs.py +113 -51
  65. toil/lib/url.py +14 -23
  66. toil/lib/web.py +7 -2
  67. toil/options/common.py +18 -15
  68. toil/options/cwl.py +2 -2
  69. toil/options/runner.py +9 -5
  70. toil/options/wdl.py +1 -3
  71. toil/provisioners/__init__.py +9 -9
  72. toil/provisioners/abstractProvisioner.py +22 -20
  73. toil/provisioners/aws/__init__.py +20 -14
  74. toil/provisioners/aws/awsProvisioner.py +10 -8
  75. toil/provisioners/clusterScaler.py +19 -18
  76. toil/provisioners/gceProvisioner.py +2 -3
  77. toil/provisioners/node.py +11 -13
  78. toil/realtimeLogger.py +4 -4
  79. toil/resource.py +5 -5
  80. toil/server/app.py +2 -2
  81. toil/server/cli/wes_cwl_runner.py +11 -11
  82. toil/server/utils.py +18 -21
  83. toil/server/wes/abstract_backend.py +9 -8
  84. toil/server/wes/amazon_wes_utils.py +3 -3
  85. toil/server/wes/tasks.py +3 -5
  86. toil/server/wes/toil_backend.py +17 -21
  87. toil/server/wsgi_app.py +3 -3
  88. toil/serviceManager.py +3 -4
  89. toil/statsAndLogging.py +12 -13
  90. toil/test/__init__.py +33 -24
  91. toil/test/batchSystems/batchSystemTest.py +12 -11
  92. toil/test/batchSystems/batch_system_plugin_test.py +3 -5
  93. toil/test/batchSystems/test_slurm.py +38 -24
  94. toil/test/cwl/conftest.py +5 -6
  95. toil/test/cwl/cwlTest.py +194 -78
  96. toil/test/cwl/download_file_uri.json +6 -0
  97. toil/test/cwl/download_file_uri_no_hostname.json +6 -0
  98. toil/test/docs/scripts/tutorial_staging.py +1 -0
  99. toil/test/jobStores/jobStoreTest.py +9 -7
  100. toil/test/lib/aws/test_iam.py +1 -3
  101. toil/test/lib/aws/test_s3.py +1 -1
  102. toil/test/lib/dockerTest.py +9 -9
  103. toil/test/lib/test_ec2.py +12 -11
  104. toil/test/lib/test_history.py +4 -4
  105. toil/test/lib/test_trs.py +16 -14
  106. toil/test/lib/test_url.py +7 -6
  107. toil/test/lib/url_plugin_test.py +12 -18
  108. toil/test/provisioners/aws/awsProvisionerTest.py +10 -8
  109. toil/test/provisioners/clusterScalerTest.py +2 -5
  110. toil/test/provisioners/clusterTest.py +1 -3
  111. toil/test/server/serverTest.py +13 -4
  112. toil/test/sort/restart_sort.py +2 -6
  113. toil/test/sort/sort.py +3 -8
  114. toil/test/src/deferredFunctionTest.py +7 -7
  115. toil/test/src/environmentTest.py +1 -2
  116. toil/test/src/fileStoreTest.py +5 -5
  117. toil/test/src/importExportFileTest.py +5 -6
  118. toil/test/src/jobServiceTest.py +22 -14
  119. toil/test/src/jobTest.py +121 -25
  120. toil/test/src/miscTests.py +5 -7
  121. toil/test/src/promisedRequirementTest.py +8 -7
  122. toil/test/src/regularLogTest.py +2 -3
  123. toil/test/src/resourceTest.py +5 -8
  124. toil/test/src/restartDAGTest.py +5 -6
  125. toil/test/src/resumabilityTest.py +2 -2
  126. toil/test/src/retainTempDirTest.py +3 -3
  127. toil/test/src/systemTest.py +3 -3
  128. toil/test/src/threadingTest.py +1 -1
  129. toil/test/src/workerTest.py +1 -2
  130. toil/test/utils/toilDebugTest.py +6 -4
  131. toil/test/utils/toilKillTest.py +1 -1
  132. toil/test/utils/utilsTest.py +15 -14
  133. toil/test/wdl/wdltoil_test.py +247 -124
  134. toil/test/wdl/wdltoil_test_kubernetes.py +2 -2
  135. toil/toilState.py +2 -3
  136. toil/utils/toilDebugFile.py +3 -8
  137. toil/utils/toilDebugJob.py +1 -2
  138. toil/utils/toilLaunchCluster.py +1 -2
  139. toil/utils/toilSshCluster.py +2 -0
  140. toil/utils/toilStats.py +19 -24
  141. toil/utils/toilStatus.py +11 -14
  142. toil/version.py +10 -10
  143. toil/wdl/wdltoil.py +313 -209
  144. toil/worker.py +18 -12
  145. {toil-9.1.1.dist-info → toil-9.2.0.dist-info}/METADATA +11 -14
  146. {toil-9.1.1.dist-info → toil-9.2.0.dist-info}/RECORD +150 -153
  147. {toil-9.1.1.dist-info → toil-9.2.0.dist-info}/WHEEL +1 -1
  148. toil/test/cwl/staging_cat.cwl +0 -27
  149. toil/test/cwl/staging_make_file.cwl +0 -25
  150. toil/test/cwl/staging_workflow.cwl +0 -43
  151. toil/test/cwl/zero_default.cwl +0 -61
  152. toil/test/utils/ABCWorkflowDebug/ABC.txt +0 -1
  153. {toil-9.1.1.dist-info → toil-9.2.0.dist-info}/entry_points.txt +0 -0
  154. {toil-9.1.1.dist-info → toil-9.2.0.dist-info}/licenses/LICENSE +0 -0
  155. {toil-9.1.1.dist-info → toil-9.2.0.dist-info}/top_level.txt +0 -0
@@ -13,21 +13,21 @@
13
13
  # limitations under the License.
14
14
  import codecs
15
15
  import logging
16
- from pathlib import Path
17
16
  import random
18
17
  import sys
19
18
  import time
20
19
  import traceback
21
- from typing import cast, Any, Literal
20
+ from pathlib import Path
22
21
  from threading import Event, Thread
22
+ from typing import Any, Literal, cast
23
23
 
24
24
  import pytest
25
25
 
26
+ from toil.batchSystems import DeadlockException
26
27
  from toil.batchSystems.singleMachine import SingleMachineBatchSystem
27
28
  from toil.exceptions import FailedJobsException
28
29
  from toil.job import Job, ServiceHostJob
29
30
  from toil.jobStores.abstractJobStore import AbstractJobStore
30
- from toil.batchSystems import DeadlockException
31
31
  from toil.test import pslow as slow
32
32
 
33
33
  logger = logging.getLogger(__name__)
@@ -177,9 +177,16 @@ class TestJobService:
177
177
  total_jobs = BUNDLE_SIZE * BUNDLE_COUNT * 2 + 1
178
178
  p_complete_job_failure = FAIL_FRACTION ** (RETRY_COUNT + 1)
179
179
  p_workflow_success = (1 - p_complete_job_failure) ** total_jobs
180
- logger.info("Going to run %s total jobs, each of which completely fails %s of the time, so the workflow will succeed with probability %s", total_jobs, p_complete_job_failure, p_workflow_success)
180
+ logger.info(
181
+ "Going to run %s total jobs, each of which completely fails %s of the time, so the workflow will succeed with probability %s",
182
+ total_jobs,
183
+ p_complete_job_failure,
184
+ p_workflow_success,
185
+ )
181
186
  p_test_failure = (1 - p_workflow_success) ** MAX_ATTEMPTS
182
- logger.info("This test will fail spuriously with probability %s", p_test_failure)
187
+ logger.info(
188
+ "This test will fail spuriously with probability %s", p_test_failure
189
+ )
183
190
 
184
191
  # We want to run the workflow through several times to test restarting, so we need it to often fail but reliably sometimes succeed, and almost always succeed when repeated.
185
192
 
@@ -192,7 +199,8 @@ class TestJobService:
192
199
  outFiles = [tmp_path / f"test{test}_{j}" for j in range(BUNDLE_COUNT)]
193
200
  # We send 3 messages each in 2 sets, each of which needs a service and a client
194
201
  messageBundles = [
195
- [random.randint(1, sys.maxsize) for i in range(BUNDLE_SIZE)] for j in range(BUNDLE_COUNT)
202
+ [random.randint(1, sys.maxsize) for i in range(BUNDLE_SIZE)]
203
+ for j in range(BUNDLE_COUNT)
196
204
  ]
197
205
  # Wire up the services/jobs
198
206
  t = Job.wrapJobFn(
@@ -247,7 +255,12 @@ class TestJobService:
247
255
  break
248
256
  except FailedJobsException as e:
249
257
  i = e.numberOfFailedJobs
250
- logger.info("Workflow attempt %s/%s failed with %s failed jobs", total_tries, max_attempts, i)
258
+ logger.info(
259
+ "Workflow attempt %s/%s failed with %s failed jobs",
260
+ total_tries,
261
+ max_attempts,
262
+ i,
263
+ )
251
264
  if total_tries == max_attempts:
252
265
  pytest.fail(reason="Exceeded a reasonable number of restarts")
253
266
  total_tries += 1
@@ -261,10 +274,7 @@ class TestPerfectServicet(TestJobService):
261
274
  Let us run all the tests in the other service test class, but without worker failures.
262
275
  """
263
276
  kwargs["badWorker"] = 0
264
- super().runToil(
265
- *args,
266
- **kwargs
267
- )
277
+ super().runToil(*args, **kwargs)
268
278
 
269
279
 
270
280
  def serviceTest(job: Job, outFile: Path, messageInt: int) -> None:
@@ -278,9 +288,7 @@ def serviceTest(job: Job, outFile: Path, messageInt: int) -> None:
278
288
  # the serviceAccessor, to prove that when service test is checkpointed and
279
289
  # restarted there is never a connection made between an earlier service and
280
290
  # later serviceAccessor, or vice versa.
281
- to_subtract = random.randint(
282
- 1, sys.maxsize
283
- )
291
+ to_subtract = random.randint(1, sys.maxsize)
284
292
  job.addChildJobFn(
285
293
  serviceAccessor,
286
294
  job.addService(ToyService(messageInt + to_subtract)),
toil/test/src/jobTest.py CHANGED
@@ -11,13 +11,12 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
- from collections.abc import Callable
15
14
  import collections
16
- import logging
17
15
  import os
18
- from pathlib import Path
19
16
  import random
20
- from typing import cast, Optional, Union, Any, NoReturn
17
+ from collections.abc import Callable
18
+ from pathlib import Path
19
+ from typing import Any, Callable, NoReturn, cast
21
20
 
22
21
  import pytest
23
22
 
@@ -28,14 +27,12 @@ from toil.job import (
28
27
  Job,
29
28
  JobFunctionWrappingJob,
30
29
  JobGraphDeadlockException,
31
- ServiceHostJob,
32
30
  Promise,
31
+ ServiceHostJob,
33
32
  )
34
33
  from toil.lib.misc import FileDescriptorOrPath
35
34
  from toil.test import pslow as slow
36
35
 
37
- from pytest_subtests import SubTests
38
-
39
36
 
40
37
  class TestJob:
41
38
  """Tests the job class."""
@@ -155,6 +152,63 @@ class TestJob:
155
152
  with pytest.raises(FailedJobsException):
156
153
  toil.start(i)
157
154
 
155
+ def testDeadlockDetectionStatic(self) -> None:
156
+ """
157
+ Test cycle detection on small example graphs.
158
+ """
159
+ # Test 1: Simple 3-node graph with a child cycle: 0 -> 1 -> 2 -> 0
160
+ childEdges: set[tuple[int, int]] = {(0, 1), (1, 2)}
161
+ followOnEdges: set[tuple[int, int]] = set()
162
+ # Valid DAG should pass
163
+ rootJob = self.makeJobGraph(3, childEdges, followOnEdges, None, False)
164
+ rootJob.checkJobGraphAcylic()
165
+ rootJob.checkJobGraphConnected()
166
+ # Adding edge (2, 0) creates cycle
167
+ childEdges.add((2, 0))
168
+ with pytest.raises(JobGraphDeadlockException):
169
+ self.makeJobGraph(3, childEdges, followOnEdges, None, False).checkJobGraphAcylic()
170
+
171
+ # Test 2: Self-loop (0 -> 0)
172
+ childEdges = {(0, 1), (0, 0)}
173
+ with pytest.raises(JobGraphDeadlockException):
174
+ self.makeJobGraph(2, childEdges, followOnEdges, None, False).checkJobGraphAcylic()
175
+
176
+ # Test 3: Multiple roots detection (nodes 0 and 2 are both roots)
177
+ childEdges = {(0, 1), (2, 1)} # Both 0 and 2 point to 1, neither has incoming edges
178
+ with pytest.raises(JobGraphDeadlockException):
179
+ self.makeJobGraph(3, childEdges, followOnEdges, None, False).checkJobGraphConnected()
180
+
181
+ # Test 4: Follow-on creating a cycle
182
+ # Graph: 0 -> 1, with follow-on 1 -> 0
183
+ childEdges = {(0, 1)}
184
+ followOnEdges = {(1, 0)}
185
+ with pytest.raises(JobGraphDeadlockException):
186
+ self.makeJobGraph(2, childEdges, followOnEdges, None, False).checkJobGraphAcylic()
187
+
188
+ # Test 5: Self follow-on
189
+ childEdges = {(0, 1)}
190
+ followOnEdges = {(0, 0)}
191
+ with pytest.raises(JobGraphDeadlockException):
192
+ self.makeJobGraph(2, childEdges, followOnEdges, None, False).checkJobGraphAcylic()
193
+
194
+ # Test 6: Larger graph - 5 nodes in a chain, add back-edge
195
+ childEdges = {(0, 1), (1, 2), (2, 3), (3, 4)}
196
+ followOnEdges = set()
197
+ rootJob = self.makeJobGraph(5, childEdges, followOnEdges, None, False)
198
+ rootJob.checkJobGraphAcylic()
199
+ # Add back-edge from 4 to 2
200
+ childEdges.add((4, 2))
201
+ with pytest.raises(JobGraphDeadlockException):
202
+ self.makeJobGraph(5, childEdges, followOnEdges, None, False).checkJobGraphAcylic()
203
+
204
+ # We have this marked as xfail but allow it to pass also, to try and
205
+ # collect failing cases without breaking unrelated PRs.
206
+ # TODO: In the future, go through the CI logs and find the cases that
207
+ # manage to fail. No failure was found in many iterations locally.
208
+ @pytest.mark.xfail(
209
+ reason="Flaky test - see https://github.com/DataBiosphere/toil/issues/5354",
210
+ strict=False,
211
+ )
158
212
  def testDeadlockDetection(self) -> None:
159
213
  """
160
214
  Randomly generate job graphs with various types of cycle in them and
@@ -189,17 +243,31 @@ class TestJob:
189
243
  rootJob2 = self.makeJobGraph(
190
244
  nodeNumber + 1, childEdges2, followOnEdges, None, False
191
245
  )
192
- with pytest.raises(JobGraphDeadlockException):
193
- rootJob2.checkJobGraphConnected()
246
+ self._assertDeadlockDetected(
247
+ rootJob2.checkJobGraphConnected,
248
+ "multiple roots",
249
+ nodeNumber + 1,
250
+ childEdges2,
251
+ followOnEdges,
252
+ )
194
253
 
195
254
  def checkChildEdgeCycleDetection(fNode: int, tNode: int) -> None:
196
255
  childEdges.add((fNode, tNode)) # Create a cycle
197
256
  adjacencyList[fNode].add(tNode)
198
- assert not self.isAcyclic(adjacencyList)
199
- with pytest.raises(JobGraphDeadlockException):
200
- self.makeJobGraph(
257
+ assert not self.isAcyclic(adjacencyList), (
258
+ f"isAcyclic incorrectly returned True after adding cycle edge "
259
+ f"({fNode}, {tNode}). nodeNumber={nodeNumber}, "
260
+ f"childEdges={childEdges}, followOnEdges={followOnEdges}"
261
+ )
262
+ self._assertDeadlockDetected(
263
+ lambda: self.makeJobGraph(
201
264
  nodeNumber, childEdges, followOnEdges, None
202
- ).checkJobGraphAcylic()
265
+ ).checkJobGraphAcylic(),
266
+ f"child edge cycle ({fNode}, {tNode})",
267
+ nodeNumber,
268
+ childEdges,
269
+ followOnEdges,
270
+ )
203
271
  # Remove the edges
204
272
  childEdges.remove((fNode, tNode))
205
273
  adjacencyList[fNode].remove(tNode)
@@ -210,10 +278,15 @@ class TestJob:
210
278
 
211
279
  def checkFollowOnEdgeCycleDetection(fNode: int, tNode: int) -> None:
212
280
  followOnEdges.add((fNode, tNode)) # Create a cycle
213
- with pytest.raises(JobGraphDeadlockException):
214
- self.makeJobGraph(
281
+ self._assertDeadlockDetected(
282
+ lambda: self.makeJobGraph(
215
283
  nodeNumber, childEdges, followOnEdges, None, False
216
- ).checkJobGraphAcylic()
284
+ ).checkJobGraphAcylic(),
285
+ f"follow-on edge cycle ({fNode}, {tNode})",
286
+ nodeNumber,
287
+ childEdges,
288
+ followOnEdges,
289
+ )
217
290
  # Remove the edges
218
291
  followOnEdges.remove((fNode, tNode))
219
292
  # Check is now acyclic again
@@ -255,10 +328,33 @@ class TestJob:
255
328
  ):
256
329
  checkFollowOnEdgeCycleDetection(fNode, tNode)
257
330
 
331
+ def _assertDeadlockDetected(
332
+ self,
333
+ check_fn: Callable[[], None],
334
+ description: str,
335
+ nodeNumber: int,
336
+ childEdges: set[tuple[int, int]],
337
+ followOnEdges: set[tuple[int, int]],
338
+ ) -> None:
339
+ """
340
+ Assert that check_fn raises JobGraphDeadlockException.
341
+
342
+ If it doesn't, provide detailed graph info for reproduction.
343
+ """
344
+ try:
345
+ check_fn()
346
+ except JobGraphDeadlockException:
347
+ return # Expected
348
+ pytest.fail(
349
+ f"JobGraphDeadlockException not raised for {description}. "
350
+ f"Graph info for reproduction: nodeNumber={nodeNumber}, "
351
+ f"childEdges={repr(childEdges)}, followOnEdges={repr(followOnEdges)}"
352
+ )
353
+
258
354
  @slow
259
355
  @pytest.mark.slow
260
356
  def testNewCheckpointIsLeafVertexNonRootCase(
261
- self, tmp_path: Path, subtests: SubTests
357
+ self, tmp_path: Path, subtests: pytest.Subtests
262
358
  ) -> None:
263
359
  """
264
360
  Test for issue #1465: Detection of checkpoint jobs that are not leaf vertices
@@ -285,7 +381,7 @@ class TestJob:
285
381
  @slow
286
382
  @pytest.mark.slow
287
383
  def testNewCheckpointIsLeafVertexRootCase(
288
- self, tmp_path: Path, subtests: SubTests
384
+ self, tmp_path: Path, subtests: pytest.Subtests
289
385
  ) -> None:
290
386
  """
291
387
  Test for issue #1466: Detection of checkpoint jobs that are not leaf vertices
@@ -307,7 +403,7 @@ class TestJob:
307
403
  def runNewCheckpointIsLeafVertexTest(
308
404
  self,
309
405
  tmp_path: Path,
310
- subtests: SubTests,
406
+ subtests: pytest.Subtests,
311
407
  createWorkflowFn: Callable[[], tuple[Job, Job]],
312
408
  ) -> None:
313
409
  """
@@ -369,10 +465,10 @@ class TestJob:
369
465
  workflowRootJob: Job,
370
466
  checkpointJob: Job,
371
467
  tmp_path: Path,
372
- checkpointJobService: Optional[Job.Service] = None,
373
- checkpointJobChild: Optional[Job] = None,
374
- checkpointJobFollowOn: Optional[Job] = None,
375
- expectedException: Optional[type[Exception]] = None,
468
+ checkpointJobService: Job.Service | None = None,
469
+ checkpointJobChild: Job | None = None,
470
+ checkpointJobFollowOn: Job | None = None,
471
+ expectedException: type[Exception] | None = None,
376
472
  ) -> None:
377
473
  """
378
474
  Modifies the checkpoint job according to the given parameters
@@ -590,7 +686,7 @@ class TestJob:
590
686
  nodeNumber: int,
591
687
  childEdges: set[tuple[int, int]],
592
688
  followOnEdges: set[tuple[int, int]],
593
- outPath: Optional[Path],
689
+ outPath: Path | None,
594
690
  addServices: bool = True,
595
691
  ) -> Job:
596
692
  """
@@ -666,7 +762,7 @@ class TestJob:
666
762
  list.
667
763
  """
668
764
 
669
- def cyclic(fNode: int, visited: set[int], stack: list[int]) -> Union[bool, int]:
765
+ def cyclic(fNode: int, visited: set[int], stack: list[int]) -> bool | int:
670
766
  if fNode not in visited:
671
767
  visited.add(fNode)
672
768
  assert fNode not in stack
@@ -14,13 +14,13 @@
14
14
  import inspect
15
15
  import logging
16
16
  import os
17
- import re
18
- from pathlib import Path
19
17
  import random
18
+ import re
20
19
  import sys
21
- from types import FrameType
20
+ from pathlib import Path
22
21
  from uuid import uuid4
23
- from typing import cast, Union
22
+
23
+ import pytest
24
24
 
25
25
  from toil.common import getNodeID
26
26
  from toil.lib.exceptions import panic, raise_
@@ -28,8 +28,6 @@ from toil.lib.io import AtomicFileCreate, atomic_install, atomic_tmp_file, mkdte
28
28
  from toil.lib.misc import CalledProcessErrorStderr, StrPath, call_command
29
29
  from toil.test import pslow as slow
30
30
 
31
- import pytest
32
-
33
31
  log = logging.getLogger(__name__)
34
32
  logging.basicConfig()
35
33
 
@@ -63,7 +61,7 @@ class TestMisc:
63
61
  # a list of the directories used in the test
64
62
  directories: list[StrPath] = [tmp_path]
65
63
  # A dict of {FILENAME: FILESIZE or FILELINK} for all files used in the test
66
- files: dict[Path, Union[int, str]] = {}
64
+ files: dict[Path, int | str] = {}
67
65
  # Create a random directory structure
68
66
  for i in range(0, 10):
69
67
  directories.append(mkdtemp(dir=random.choice(directories), prefix="test"))
@@ -12,22 +12,23 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- from collections.abc import Generator
16
15
  import argparse
17
16
  import logging
18
17
  import os
19
- from pathlib import Path
20
18
  import time
19
+ from collections.abc import Generator
20
+ from pathlib import Path
21
21
  from typing import Any
22
22
 
23
- from toil.test.batchSystems import batchSystemTest
23
+ import pytest
24
+
24
25
  from toil.batchSystems.mesos.test import MesosTestSupport
25
26
  from toil.fileStores import FileID
26
- from toil.job import Job, PromisedRequirement, JobFunctionWrappingJob, Promise
27
+ from toil.job import Job, JobFunctionWrappingJob, Promise, PromisedRequirement
27
28
  from toil.lib.retry import retry_flaky_test
28
- from toil.test import pneeds_mesos as needs_mesos, pslow as slow
29
-
30
- import pytest
29
+ from toil.test import pneeds_mesos as needs_mesos
30
+ from toil.test import pslow as slow
31
+ from toil.test.batchSystems import batchSystemTest
31
32
 
32
33
  log = logging.getLogger(__name__)
33
34
 
@@ -14,10 +14,9 @@
14
14
  import logging
15
15
  import mimetypes
16
16
  import os
17
- from pathlib import Path
18
17
  import subprocess
19
18
  import sys
20
- from typing import Optional
19
+ from pathlib import Path
21
20
 
22
21
  from toil.test import pslow as slow
23
22
  from toil.test.mesos import helloWorld
@@ -36,7 +35,7 @@ class RegularLogTest:
36
35
  ]
37
36
 
38
37
  def _assertFileTypeExists(
39
- self, dirpath: Path, extension: str, encoding: Optional[str] = None
38
+ self, dirpath: Path, extension: str, encoding: str | None = None
40
39
  ) -> None:
41
40
  # an encoding of None implies no compression
42
41
  logger.info("Checking for %s file in %s", extension, dirpath)
@@ -11,27 +11,24 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
- from collections.abc import Iterable
15
14
  import importlib
16
15
  import os
17
- from pathlib import Path
18
16
  import subprocess
19
17
  import sys
20
- import tempfile
21
- from contextlib import contextmanager
18
+ from collections.abc import Iterable
22
19
  from inspect import getsource
23
20
  from io import BytesIO
21
+ from pathlib import Path
24
22
  from textwrap import dedent
25
23
  from unittest.mock import MagicMock, patch
26
24
  from zipfile import ZipFile
27
- from typing import Optional
25
+
26
+ import pytest
28
27
 
29
28
  from toil import inVirtualEnv
30
29
  from toil.resource import ModuleDescriptor, Resource, ResourceException
31
30
  from toil.version import exactPython
32
31
 
33
- import pytest
34
-
35
32
 
36
33
  def tempFileContaining(directory: Path, content: str, suffix: str = "") -> str:
37
34
  """
@@ -151,7 +148,7 @@ class TestResource:
151
148
  self,
152
149
  module_name: str,
153
150
  shouldBelongToToil: bool = False,
154
- expectedContents: Optional[Iterable[str]] = None,
151
+ expectedContents: Iterable[str] | None = None,
155
152
  allowExtraContents: bool = True,
156
153
  ) -> None:
157
154
  module = ModuleDescriptor.forModule(module_name)
@@ -15,17 +15,16 @@
15
15
 
16
16
  import logging
17
17
  import os
18
- from pathlib import Path
19
18
  import signal
20
- from typing import Optional
19
+ from pathlib import Path
20
+
21
+ import pytest
21
22
 
22
23
  from toil.common import Toil
23
24
  from toil.exceptions import FailedJobsException
24
25
  from toil.job import Job
25
26
  from toil.test import pslow as slow
26
27
 
27
- import pytest
28
-
29
28
  logger = logging.getLogger(__name__)
30
29
 
31
30
 
@@ -89,7 +88,7 @@ class TestRestartDAG:
89
88
 
90
89
  assert not childFile.exists()
91
90
 
92
- errorRaised: Optional[BaseException] = None
91
+ errorRaised: BaseException | None = None
93
92
  # Run the test
94
93
  for runMode in "start", "restart":
95
94
  errorRaised = None
@@ -142,7 +141,7 @@ class TestRestartDAG:
142
141
  )
143
142
 
144
143
 
145
- def passingFn(job: Job, file: Optional[Path] = None) -> None:
144
+ def passingFn(job: Job, file: Path | None = None) -> None:
146
145
  """
147
146
  This function is guaranteed to pass as it does nothing out of the ordinary.
148
147
 
@@ -14,13 +14,13 @@
14
14
 
15
15
  from pathlib import Path
16
16
 
17
+ import pytest
18
+
17
19
  from toil.exceptions import FailedJobsException
18
20
  from toil.job import Job, JobFunctionWrappingJob
19
21
  from toil.jobStores.abstractJobStore import NoSuchFileException
20
22
  from toil.test import pslow as slow
21
23
 
22
- import pytest
23
-
24
24
 
25
25
  class TestResumability:
26
26
  """
@@ -12,15 +12,15 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
  import argparse
15
- from collections.abc import Callable
16
15
  import os
16
+ from collections.abc import Callable
17
17
  from pathlib import Path
18
18
 
19
+ import pytest
20
+
19
21
  from toil.exceptions import FailedJobsException
20
22
  from toil.job import Job, JobFunctionWrappingJob
21
23
 
22
- import pytest
23
-
24
24
 
25
25
  class TestCleanWorkDir:
26
26
  """
@@ -1,9 +1,9 @@
1
1
  import errno
2
2
  import multiprocessing
3
3
  import os
4
- from pathlib import Path
5
4
  from functools import partial
6
- from typing import Any, Optional
5
+ from pathlib import Path
6
+ from typing import Any
7
7
 
8
8
  from toil.lib.io import mkdtemp
9
9
  from toil.lib.threading import cpu_count
@@ -44,7 +44,7 @@ class TestSystem:
44
44
 
45
45
  def _testAtomicityOfNonEmptyDirectoryRenamesTask(
46
46
  parent: Path, child: Path, _: Any
47
- ) -> Optional[int]:
47
+ ) -> int | None:
48
48
  tmpChildDir = mkdtemp(dir=parent, prefix="child", suffix=".tmp")
49
49
  grandChild = os.path.join(tmpChildDir, "grandChild")
50
50
  open(grandChild, "w").close()
@@ -1,11 +1,11 @@
1
1
  import logging
2
2
  import multiprocessing
3
3
  import os
4
- from pathlib import Path
5
4
  import random
6
5
  import time
7
6
  import traceback
8
7
  from functools import partial
8
+ from pathlib import Path
9
9
 
10
10
  from toil.lib.threading import LastProcessStandingArena, cpu_count, global_mutex
11
11
 
@@ -12,7 +12,6 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- from typing import Optional
16
15
 
17
16
  from toil.common import Config
18
17
  from toil.job import CheckpointJobDescription, JobDescription
@@ -42,7 +41,7 @@ class WorkerTests(ToilTest):
42
41
  disk,
43
42
  preemptible: bool = True,
44
43
  checkpoint: bool = False,
45
- local: Optional[bool] = None,
44
+ local: bool | None = None,
46
45
  ):
47
46
  """
48
47
  Create a JobDescription with no command (representing a Job that
@@ -13,15 +13,17 @@
13
13
  # limitations under the License.
14
14
  import logging
15
15
  import os
16
- from pathlib import Path
17
16
  import subprocess
17
+ from pathlib import Path
18
+
19
+ import pytest
18
20
 
19
21
  from toil.lib.resources import glob
20
- from toil.test import get_data, pneeds_wdl as needs_wdl, pslow as slow
22
+ from toil.test import get_data
23
+ from toil.test import pneeds_wdl as needs_wdl
24
+ from toil.test import pslow as slow
21
25
  from toil.version import python
22
26
 
23
- import pytest
24
-
25
27
  logger = logging.getLogger(__name__)
26
28
 
27
29
 
@@ -14,11 +14,11 @@
14
14
 
15
15
  import logging
16
16
  import os
17
- from pathlib import Path
18
17
  import subprocess
19
18
  import sys
20
19
  import time
21
20
  import unittest
21
+ from pathlib import Path
22
22
 
23
23
  from toil.common import Toil
24
24
  from toil.jobStores.abstractJobStore import NoSuchFileException, NoSuchJobStoreException