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
toil/test/__init__.py CHANGED
@@ -1,4 +1,5 @@
1
1
  """Base testing class for Toil."""
2
+
2
3
  # Copyright (C) 2015-2021 Regents of the University of California
3
4
  #
4
5
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,43 +21,28 @@ import re
20
21
  import shutil
21
22
  import signal
22
23
  import subprocess
23
- import sys
24
24
  import threading
25
25
  import time
26
26
  import unittest
27
27
  import uuid
28
+ import zoneinfo
28
29
  from abc import ABCMeta, abstractmethod
30
+ from collections.abc import Generator
29
31
  from contextlib import contextmanager
30
32
  from inspect import getsource
31
33
  from shutil import which
32
34
  from tempfile import mkstemp
33
35
  from textwrap import dedent
34
- from typing import (Any,
35
- Callable,
36
- Dict,
37
- Generator,
38
- List,
39
- Optional,
40
- Tuple,
41
- Type,
42
- TypeVar,
43
- Union,
44
- cast)
36
+ from typing import Any, Callable, Literal, Optional, TypeVar, Union, cast
45
37
  from unittest.util import strclass
46
38
  from urllib.error import HTTPError, URLError
47
39
  from urllib.request import urlopen
48
40
 
49
- import pytz
50
-
51
- if sys.version_info >= (3, 8):
52
- from typing import Literal
53
- else:
54
- from typing_extensions import Literal
55
-
56
41
  from toil import ApplianceImageNotFound, applianceSelf, toilPackageDirPath
57
- from toil.lib.accelerators import (have_working_nvidia_docker_runtime,
58
- have_working_nvidia_smi)
59
- from toil.lib.aws import running_on_ec2
42
+ from toil.lib.accelerators import (
43
+ have_working_nvidia_docker_runtime,
44
+ have_working_nvidia_smi,
45
+ )
60
46
  from toil.lib.io import mkdtemp
61
47
  from toil.lib.iterables import concat
62
48
  from toil.lib.memoize import memoize
@@ -83,20 +69,24 @@ class ToilTest(unittest.TestCase):
83
69
  """
84
70
 
85
71
  _tempBaseDir: Optional[str] = None
86
- _tempDirs: List[str] = []
72
+ _tempDirs: list[str] = []
87
73
 
88
74
  def setup_method(self, method: Any) -> None:
89
- western = pytz.timezone('America/Los_Angeles')
90
- california_time = western.localize(datetime.datetime.now())
75
+ western = zoneinfo.ZoneInfo("America/Los_Angeles")
76
+ california_time = datetime.datetime.now(tz=western)
91
77
  timestamp = california_time.strftime("%b %d %Y %H:%M:%S:%f %Z")
92
- print(f"\n\n[TEST] {strclass(self.__class__)}:{self._testMethodName} ({timestamp})\n\n")
78
+ print(
79
+ f"\n\n[TEST] {strclass(self.__class__)}:{self._testMethodName} ({timestamp})\n\n"
80
+ )
93
81
 
94
82
  @classmethod
95
83
  def setUpClass(cls) -> None:
96
84
  super().setUpClass()
97
- tempBaseDir = os.environ.get('TOIL_TEST_TEMP', None)
85
+ tempBaseDir = os.environ.get("TOIL_TEST_TEMP", None)
98
86
  if tempBaseDir is not None and not os.path.isabs(tempBaseDir):
99
- tempBaseDir = os.path.abspath(os.path.join(cls._projectRootPath(), tempBaseDir))
87
+ tempBaseDir = os.path.abspath(
88
+ os.path.join(cls._projectRootPath(), tempBaseDir)
89
+ )
100
90
  os.makedirs(tempBaseDir, exist_ok=True)
101
91
  cls._tempBaseDir = tempBaseDir
102
92
 
@@ -127,7 +117,9 @@ class ToilTest(unittest.TestCase):
127
117
  Use us-west-2 unless running on EC2, in which case use the region in which
128
118
  the instance is located
129
119
  """
130
- return cls._region() if running_on_ec2() else 'us-west-2'
120
+ from toil.lib.aws import running_on_ec2
121
+
122
+ return cls._region() if running_on_ec2() else "us-west-2"
131
123
 
132
124
  @classmethod
133
125
  def _availabilityZone(cls) -> str:
@@ -151,13 +143,15 @@ class ToilTest(unittest.TestCase):
151
143
  The region will not change over the life of the instance so the result
152
144
  is memoized to avoid unnecessary work.
153
145
  """
154
- region = re.match(r'^([a-z]{2}-[a-z]+-[1-9][0-9]*)([a-z])$', cls._availabilityZone())
146
+ region = re.match(
147
+ r"^([a-z]{2}-[a-z]+-[1-9][0-9]*)([a-z])$", cls._availabilityZone()
148
+ )
155
149
  assert region
156
150
  return region.group(1)
157
151
 
158
152
  @classmethod
159
153
  def _getUtilScriptPath(cls, script_name: str) -> str:
160
- return os.path.join(toilPackageDirPath(), 'utils', script_name + '.py')
154
+ return os.path.join(toilPackageDirPath(), "utils", script_name + ".py")
161
155
 
162
156
  @classmethod
163
157
  def _projectRootPath(cls) -> str:
@@ -169,12 +163,12 @@ class ToilTest(unittest.TestCase):
169
163
  mode, since it assumes the existence of a src subdirectory which, in a regular install
170
164
  wouldn't exist. Then again, in that mode project root has no meaning anyways.
171
165
  """
172
- assert re.search(r'__init__\.pyc?$', __file__)
166
+ assert re.search(r"__init__\.pyc?$", __file__)
173
167
  projectRootPath = os.path.dirname(os.path.abspath(__file__))
174
- packageComponents = __name__.split('.')
175
- expectedSuffix = os.path.join('src', *packageComponents)
168
+ packageComponents = __name__.split(".")
169
+ expectedSuffix = os.path.join("src", *packageComponents)
176
170
  assert projectRootPath.endswith(expectedSuffix)
177
- projectRootPath = projectRootPath[:-len(expectedSuffix)]
171
+ projectRootPath = projectRootPath[: -len(expectedSuffix)]
178
172
  return projectRootPath
179
173
 
180
174
  def _createTempDir(self, purpose: Optional[str] = None) -> str:
@@ -187,7 +181,7 @@ class ToilTest(unittest.TestCase):
187
181
  classname = classname[len("toil.test.") :]
188
182
  prefix = ["toil", "test", classname]
189
183
  prefix.extend([_f for _f in names if _f])
190
- prefix.append('')
184
+ prefix.append("")
191
185
  temp_dir_path = os.path.realpath(
192
186
  mkdtemp(dir=cls._tempBaseDir, prefix="-".join(prefix))
193
187
  )
@@ -277,9 +271,9 @@ class ToilTest(unittest.TestCase):
277
271
  capture = kwargs.pop("capture", False)
278
272
  _input = kwargs.pop("input", None)
279
273
  if capture:
280
- kwargs['stdout'] = subprocess.PIPE
274
+ kwargs["stdout"] = subprocess.PIPE
281
275
  if _input is not None:
282
- kwargs['stdin'] = subprocess.PIPE
276
+ kwargs["stdin"] = subprocess.PIPE
283
277
  popen = subprocess.Popen(args, universal_newlines=True, **kwargs)
284
278
  stdout, stderr = popen.communicate(input=_input)
285
279
  assert stderr is None
@@ -295,7 +289,8 @@ class ToilTest(unittest.TestCase):
295
289
  This is a naughty but incredibly useful trick that lets you embed user scripts
296
290
  as nested functions and expose them to the syntax checker of your IDE.
297
291
  """
298
- return dedent('\n'.join(getsource(callable_).split('\n')[1:]))
292
+ return dedent("\n".join(getsource(callable_).split("\n")[1:]))
293
+
299
294
 
300
295
  MT = TypeVar("MT", bound=Callable[..., Any])
301
296
 
@@ -306,6 +301,7 @@ except ImportError:
306
301
  # noinspection PyUnusedLocal
307
302
  def _mark_test(name: str, test_item: MT) -> MT:
308
303
  return test_item
304
+
309
305
  else:
310
306
 
311
307
  def _mark_test(name: str, test_item: MT) -> MT:
@@ -319,7 +315,7 @@ def get_temp_file(suffix: str = "", rootDir: Optional[str] = None) -> str:
319
315
  os.close(handle)
320
316
  return tmp_file
321
317
  else:
322
- alphanumerics = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
318
+ alphanumerics = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
323
319
  tmp_file = os.path.join(
324
320
  rootDir,
325
321
  f"tmp_{''.join([random.choice(alphanumerics) for _ in range(0, 10)])}{suffix}",
@@ -328,18 +324,24 @@ def get_temp_file(suffix: str = "", rootDir: Optional[str] = None) -> str:
328
324
  os.chmod(tmp_file, 0o777) # Ensure everyone has access to the file.
329
325
  return tmp_file
330
326
 
327
+
331
328
  def needs_env_var(var_name: str, comment: Optional[str] = None) -> Callable[[MT], MT]:
332
329
  """
333
330
  Use as a decorator before test classes or methods to run only if the given
334
331
  environment variable is set.
335
332
  Can include a comment saying what the variable should be set to.
336
333
  """
334
+
337
335
  def decorator(test_item: MT) -> MT:
338
336
  if not os.getenv(var_name):
339
- return unittest.skip(f"Set {var_name}{' to ' + comment if comment else ''} to include this test.")(test_item)
337
+ return unittest.skip(
338
+ f"Set {var_name}{' to ' + comment if comment else ''} to include this test."
339
+ )(test_item)
340
340
  return test_item
341
+
341
342
  return decorator
342
343
 
344
+
343
345
  def needs_rsync3(test_item: MT) -> MT:
344
346
  """
345
347
  Decorate classes or methods that depend on any features from rsync version 3.0.0+.
@@ -347,50 +349,60 @@ def needs_rsync3(test_item: MT) -> MT:
347
349
  Necessary because :meth:`utilsTest.testAWSProvisionerUtils` uses option `--protect-args`
348
350
  which is only available in rsync 3
349
351
  """
350
- test_item = _mark_test('rsync', test_item)
352
+ test_item = _mark_test("rsync", test_item)
351
353
  try:
352
- versionInfo = subprocess.check_output(['rsync', '--version']).decode('utf-8')
354
+ versionInfo = subprocess.check_output(["rsync", "--version"]).decode("utf-8")
353
355
  # output looks like: 'rsync version 2.6.9 ...'
354
356
  if int(versionInfo.split()[2].split(".")[0]) < 3:
355
357
  return unittest.skip("This test depends on rsync version 3.0.0+.")(
356
358
  test_item
357
359
  )
358
360
  except subprocess.CalledProcessError:
359
- return unittest.skip('rsync needs to be installed to run this test.')(test_item)
361
+ return unittest.skip("rsync needs to be installed to run this test.")(test_item)
360
362
  return test_item
361
363
 
362
364
 
363
365
  def needs_online(test_item: MT) -> MT:
364
366
  """Use as a decorator before test classes or methods to run only if we are meant to talk to the Internet."""
365
- test_item = _mark_test('online', test_item)
366
- if os.getenv('TOIL_SKIP_ONLINE', '').lower() == 'true':
367
- return unittest.skip('Skipping online test.')(test_item)
367
+ test_item = _mark_test("online", test_item)
368
+ if os.getenv("TOIL_SKIP_ONLINE", "").lower() == "true":
369
+ return unittest.skip("Skipping online test.")(test_item)
368
370
  return test_item
369
371
 
372
+
370
373
  def needs_aws_s3(test_item: MT) -> MT:
371
374
  """Use as a decorator before test classes or methods to run only if AWS S3 is usable."""
372
375
  # TODO: we just check for generic access to the AWS account
373
- test_item = _mark_test('aws-s3', needs_online(test_item))
376
+ test_item = _mark_test("aws-s3", needs_online(test_item))
374
377
  try:
375
- from boto import config
376
- boto_credentials = config.get('Credentials', 'aws_access_key_id')
378
+ from boto3 import Session
379
+
380
+ session = Session()
381
+ boto3_credentials = session.get_credentials()
377
382
  except ImportError:
378
383
  return unittest.skip("Install Toil with the 'aws' extra to include this test.")(
379
384
  test_item
380
385
  )
381
-
382
- if not (boto_credentials or os.path.exists(os.path.expanduser('~/.aws/credentials')) or running_on_ec2()):
383
- return unittest.skip("Configure AWS credentials to include this test.")(test_item)
386
+ from toil.lib.aws import running_on_ec2
387
+
388
+ if not (
389
+ boto3_credentials
390
+ or os.path.exists(os.path.expanduser("~/.aws/credentials"))
391
+ or running_on_ec2()
392
+ ):
393
+ return unittest.skip("Configure AWS credentials to include this test.")(
394
+ test_item
395
+ )
384
396
  return test_item
385
397
 
386
398
 
387
399
  def needs_aws_ec2(test_item: MT) -> MT:
388
400
  """Use as a decorator before test classes or methods to run only if AWS EC2 is usable."""
389
401
  # Assume we need S3 as well as EC2
390
- test_item = _mark_test('aws-ec2', needs_aws_s3(test_item))
402
+ test_item = _mark_test("aws-ec2", needs_aws_s3(test_item))
391
403
  # In addition to S3 we also need an SSH key to deploy with.
392
404
  # TODO: We assume that if this is set we have EC2 access.
393
- test_item = needs_env_var('TOIL_AWS_KEYNAME', 'an AWS-stored SSH key')(test_item)
405
+ test_item = needs_env_var("TOIL_AWS_KEYNAME", "an AWS-stored SSH key")(test_item)
394
406
  return test_item
395
407
 
396
408
 
@@ -400,16 +412,23 @@ def needs_aws_batch(test_item: MT) -> MT:
400
412
  is usable.
401
413
  """
402
414
  # Assume we need S3 as well as Batch
403
- test_item = _mark_test('aws-batch', needs_aws_s3(test_item))
415
+ test_item = _mark_test("aws-batch", needs_aws_s3(test_item))
404
416
  # Assume we have Batch if the user has set these variables.
405
- test_item = needs_env_var('TOIL_AWS_BATCH_QUEUE', 'an AWS Batch queue name or ARN')(test_item)
406
- test_item = needs_env_var('TOIL_AWS_BATCH_JOB_ROLE_ARN', 'an IAM role ARN that grants S3 and SDB access')(test_item)
417
+ test_item = needs_env_var("TOIL_AWS_BATCH_QUEUE", "an AWS Batch queue name or ARN")(
418
+ test_item
419
+ )
420
+ test_item = needs_env_var(
421
+ "TOIL_AWS_BATCH_JOB_ROLE_ARN", "an IAM role ARN that grants S3 and SDB access"
422
+ )(test_item)
407
423
  try:
408
424
  from toil.lib.aws import get_current_aws_region
425
+
409
426
  if get_current_aws_region() is None:
410
427
  # We don't know a region so we need one set.
411
428
  # TODO: It always won't be set if we get here.
412
- test_item = needs_env_var('TOIL_AWS_REGION', 'an AWS region to use with AWS batch')(test_item)
429
+ test_item = needs_env_var(
430
+ "TOIL_AWS_REGION", "an AWS region to use with AWS batch"
431
+ )(test_item)
413
432
 
414
433
  except ImportError:
415
434
  return unittest.skip("Install Toil with the 'aws' extra to include this test.")(
@@ -417,67 +436,79 @@ def needs_aws_batch(test_item: MT) -> MT:
417
436
  )
418
437
  return test_item
419
438
 
439
+
420
440
  def needs_google_storage(test_item: MT) -> MT:
421
441
  """
422
442
  Use as a decorator before test classes or methods to run only if Google
423
443
  Cloud is installed and we ought to be able to access public Google Storage
424
444
  URIs.
425
445
  """
426
- test_item = _mark_test('google-storage', needs_online(test_item))
446
+ test_item = _mark_test("google-storage", needs_online(test_item))
427
447
  try:
428
448
  from google.cloud import storage # noqa
429
449
  except ImportError:
430
- return unittest.skip("Install Toil with the 'google' extra to include this test.")(test_item)
450
+ return unittest.skip(
451
+ "Install Toil with the 'google' extra to include this test."
452
+ )(test_item)
431
453
 
432
454
  return test_item
433
455
 
456
+
434
457
  def needs_google_project(test_item: MT) -> MT:
435
458
  """
436
459
  Use as a decorator before test classes or methods to run only if we have a Google Cloud project set.
437
460
  """
438
- test_item = _mark_test('google-project', needs_online(test_item))
439
- test_item = needs_env_var('TOIL_GOOGLE_PROJECTID', "a Google project ID")(test_item)
461
+ test_item = _mark_test("google-project", needs_online(test_item))
462
+ test_item = needs_env_var("TOIL_GOOGLE_PROJECTID", "a Google project ID")(test_item)
440
463
  return test_item
441
464
 
442
465
 
443
466
  def needs_gridengine(test_item: MT) -> MT:
444
467
  """Use as a decorator before test classes or methods to run only if GridEngine is installed."""
445
- test_item = _mark_test('gridengine', test_item)
446
- if which('qhost'):
468
+ test_item = _mark_test("gridengine", test_item)
469
+ if which("qhost"):
447
470
  return test_item
448
471
  return unittest.skip("Install GridEngine to include this test.")(test_item)
449
472
 
450
473
 
451
474
  def needs_torque(test_item: MT) -> MT:
452
475
  """Use as a decorator before test classes or methods to run only if PBS/Torque is installed."""
453
- test_item = _mark_test('torque', test_item)
454
- if which('pbsnodes'):
476
+ test_item = _mark_test("torque", test_item)
477
+ if which("pbsnodes"):
455
478
  return test_item
456
479
  return unittest.skip("Install PBS/Torque to include this test.")(test_item)
457
480
 
481
+
458
482
  def needs_kubernetes_installed(test_item: MT) -> MT:
459
483
  """Use as a decorator before test classes or methods to run only if Kubernetes is installed."""
460
- test_item = _mark_test('kubernetes', test_item)
484
+ test_item = _mark_test("kubernetes", test_item)
461
485
  try:
462
486
  import kubernetes
487
+
463
488
  str(kubernetes) # to prevent removal of this import
464
489
  except ImportError:
465
- return unittest.skip("Install Toil with the 'kubernetes' extra to include this test.")(test_item)
490
+ return unittest.skip(
491
+ "Install Toil with the 'kubernetes' extra to include this test."
492
+ )(test_item)
466
493
  return test_item
467
494
 
495
+
468
496
  def needs_kubernetes(test_item: MT) -> MT:
469
497
  """Use as a decorator before test classes or methods to run only if Kubernetes is installed and configured."""
470
498
  test_item = needs_kubernetes_installed(needs_online(test_item))
471
499
  try:
472
500
  import kubernetes
501
+
473
502
  try:
474
503
  kubernetes.config.load_kube_config()
475
504
  except kubernetes.config.ConfigException:
476
505
  try:
477
506
  kubernetes.config.load_incluster_config()
478
507
  except kubernetes.config.ConfigException:
479
- return unittest.skip("Configure Kubernetes (~/.kube/config, $KUBECONFIG, "
480
- "or current pod) to include this test.")(test_item)
508
+ return unittest.skip(
509
+ "Configure Kubernetes (~/.kube/config, $KUBECONFIG, "
510
+ "or current pod) to include this test."
511
+ )(test_item)
481
512
  except ImportError:
482
513
  # We should already be skipping this test
483
514
  pass
@@ -486,37 +517,50 @@ def needs_kubernetes(test_item: MT) -> MT:
486
517
 
487
518
  def needs_mesos(test_item: MT) -> MT:
488
519
  """Use as a decorator before test classes or methods to run only if Mesos is installed."""
489
- test_item = _mark_test('mesos', test_item)
490
- if not (which('mesos-master') or which('mesos-agent')):
491
- return unittest.skip("Install Mesos (and Toil with the 'mesos' extra) to include this test.")(test_item)
520
+ test_item = _mark_test("mesos", test_item)
521
+ if not (which("mesos-master") or which("mesos-agent")):
522
+ return unittest.skip(
523
+ "Install Mesos (and Toil with the 'mesos' extra) to include this test."
524
+ )(test_item)
492
525
  try:
493
526
  import psutil # noqa
494
527
  import pymesos # noqa
495
528
  except ImportError:
496
- return unittest.skip("Install Mesos (and Toil with the 'mesos' extra) to include this test.")(test_item)
529
+ return unittest.skip(
530
+ "Install Mesos (and Toil with the 'mesos' extra) to include this test."
531
+ )(test_item)
497
532
  return test_item
498
533
 
499
534
 
500
535
  def needs_slurm(test_item: MT) -> MT:
501
536
  """Use as a decorator before test classes or methods to run only if Slurm is installed."""
502
- test_item = _mark_test('slurm', test_item)
503
- if which('squeue'):
537
+ test_item = _mark_test("slurm", test_item)
538
+ if which("squeue"):
504
539
  return test_item
505
540
  return unittest.skip("Install Slurm to include this test.")(test_item)
506
541
 
507
542
 
508
543
  def needs_htcondor(test_item: MT) -> MT:
509
544
  """Use a decorator before test classes or methods to run only if the HTCondor is installed."""
510
- test_item = _mark_test('htcondor', test_item)
545
+ test_item = _mark_test("htcondor", test_item)
511
546
  try:
512
547
  import htcondor
513
- htcondor.Collector(os.getenv('TOIL_HTCONDOR_COLLECTOR')).query(constraint='False')
548
+
549
+ htcondor.Collector(os.getenv("TOIL_HTCONDOR_COLLECTOR")).query(
550
+ constraint="False"
551
+ )
514
552
  except ImportError:
515
- return unittest.skip("Install the HTCondor Python bindings to include this test.")(test_item)
553
+ return unittest.skip(
554
+ "Install the HTCondor Python bindings to include this test."
555
+ )(test_item)
516
556
  except OSError:
517
- return unittest.skip("HTCondor must be running to include this test.")(test_item)
557
+ return unittest.skip("HTCondor must be running to include this test.")(
558
+ test_item
559
+ )
518
560
  except RuntimeError:
519
- return unittest.skip("HTCondor must be installed and configured to include this test.")(test_item)
561
+ return unittest.skip(
562
+ "HTCondor must be installed and configured to include this test."
563
+ )(test_item)
520
564
  else:
521
565
  return test_item
522
566
 
@@ -525,8 +569,8 @@ def needs_lsf(test_item: MT) -> MT:
525
569
  """
526
570
  Use as a decorator before test classes or methods to only run them if LSF is installed.
527
571
  """
528
- test_item = _mark_test('lsf', test_item)
529
- if which('bsub'):
572
+ test_item = _mark_test("lsf", test_item)
573
+ if which("bsub"):
530
574
  return test_item
531
575
  else:
532
576
  return unittest.skip("Install LSF to include this test.")(test_item)
@@ -534,8 +578,8 @@ def needs_lsf(test_item: MT) -> MT:
534
578
 
535
579
  def needs_java(test_item: MT) -> MT:
536
580
  """Use as a test decorator to run only if java is installed."""
537
- test_item = _mark_test('java', test_item)
538
- if which('java'):
581
+ test_item = _mark_test("java", test_item)
582
+ if which("java"):
539
583
  return test_item
540
584
  else:
541
585
  return unittest.skip("Install java to include this test.")(test_item)
@@ -546,74 +590,84 @@ def needs_docker(test_item: MT) -> MT:
546
590
  Use as a decorator before test classes or methods to only run them if
547
591
  docker is installed and docker-based tests are enabled.
548
592
  """
549
- test_item = _mark_test('docker', needs_online(test_item))
550
- if os.getenv('TOIL_SKIP_DOCKER', '').lower() == 'true':
551
- return unittest.skip('Skipping docker test.')(test_item)
552
- if which('docker'):
593
+ test_item = _mark_test("docker", needs_online(test_item))
594
+ if os.getenv("TOIL_SKIP_DOCKER", "").lower() == "true":
595
+ return unittest.skip("Skipping docker test.")(test_item)
596
+ if which("docker"):
553
597
  return test_item
554
598
  else:
555
599
  return unittest.skip("Install docker to include this test.")(test_item)
556
600
 
601
+
557
602
  def needs_singularity(test_item: MT) -> MT:
558
603
  """
559
604
  Use as a decorator before test classes or methods to only run them if
560
605
  singularity is installed.
561
606
  """
562
- test_item = _mark_test('singularity', needs_online(test_item))
563
- if which('singularity'):
607
+ test_item = _mark_test("singularity", needs_online(test_item))
608
+ if which("singularity"):
564
609
  return test_item
565
610
  else:
566
611
  return unittest.skip("Install singularity to include this test.")(test_item)
567
612
 
613
+
568
614
  def needs_singularity_or_docker(test_item: MT) -> MT:
569
615
  """
570
616
  Use as a decorator before test classes or methods to only run them if
571
617
  docker is installed and docker-based tests are enabled, or if Singularity
572
618
  is installed.
573
619
  """
574
-
620
+
575
621
  # TODO: Is there a good way to OR decorators?
576
- if which('singularity'):
622
+ if which("singularity"):
577
623
  # Singularity is here, say it's a Singularity test
578
624
  return needs_singularity(test_item)
579
625
  else:
580
626
  # Otherwise say it's a Docker test.
581
627
  return needs_docker(test_item)
582
-
628
+
629
+
583
630
  def needs_local_cuda(test_item: MT) -> MT:
584
631
  """
585
632
  Use as a decorator before test classes or methods to only run them if
586
633
  a CUDA setup legible to cwltool (i.e. providing userspace nvidia-smi) is present.
587
634
  """
588
- test_item = _mark_test('local_cuda', test_item)
635
+ test_item = _mark_test("local_cuda", test_item)
589
636
  if have_working_nvidia_smi():
590
637
  return test_item
591
638
  else:
592
- return unittest.skip("Install nvidia-smi, an nvidia proprietary driver, and a CUDA-capable nvidia GPU to include this test.")(test_item)
639
+ return unittest.skip(
640
+ "Install nvidia-smi, an nvidia proprietary driver, and a CUDA-capable nvidia GPU to include this test."
641
+ )(test_item)
642
+
593
643
 
594
644
  def needs_docker_cuda(test_item: MT) -> MT:
595
645
  """
596
646
  Use as a decorator before test classes or methods to only run them if
597
647
  a CUDA setup is available through Docker.
598
648
  """
599
- test_item = _mark_test('docker_cuda', needs_online(test_item))
649
+ test_item = _mark_test("docker_cuda", needs_online(test_item))
600
650
  if have_working_nvidia_docker_runtime():
601
651
  return test_item
602
652
  else:
603
- return unittest.skip("Install nvidia-container-runtime on your Docker server and configure an 'nvidia' runtime to include this test.")(test_item)
653
+ return unittest.skip(
654
+ "Install nvidia-container-runtime on your Docker server and configure an 'nvidia' runtime to include this test."
655
+ )(test_item)
656
+
604
657
 
605
658
  def needs_encryption(test_item: MT) -> MT:
606
659
  """
607
660
  Use as a decorator before test classes or methods to only run them if PyNaCl is installed
608
661
  and configured.
609
662
  """
610
- test_item = _mark_test('encryption', test_item)
663
+ test_item = _mark_test("encryption", test_item)
611
664
  try:
612
665
  # noinspection PyUnresolvedReferences
613
666
  import nacl # noqa
614
667
  except ImportError:
615
668
  return unittest.skip(
616
- "Install Toil with the 'encryption' extra to include this test.")(test_item)
669
+ "Install Toil with the 'encryption' extra to include this test."
670
+ )(test_item)
617
671
  else:
618
672
  return test_item
619
673
 
@@ -623,12 +677,31 @@ def needs_cwl(test_item: MT) -> MT:
623
677
  Use as a decorator before test classes or methods to only run them if CWLTool is installed
624
678
  and configured.
625
679
  """
626
- test_item = _mark_test('cwl', test_item)
680
+ test_item = _mark_test("cwl", test_item)
627
681
  try:
628
682
  # noinspection PyUnresolvedReferences
629
683
  import cwltool # noqa
630
684
  except ImportError:
631
- return unittest.skip("Install Toil with the 'cwl' extra to include this test.")(test_item)
685
+ return unittest.skip("Install Toil with the 'cwl' extra to include this test.")(
686
+ test_item
687
+ )
688
+ else:
689
+ return test_item
690
+
691
+
692
+ def needs_wdl(test_item: MT) -> MT:
693
+ """
694
+ Use as a decorator before test classes or methods to only run them if miniwdl is installed
695
+ and configured.
696
+ """
697
+ test_item = _mark_test("wdl", test_item)
698
+ try:
699
+ # noinspection PyUnresolvedReferences
700
+ import WDL # noqa
701
+ except ImportError:
702
+ return unittest.skip("Install Toil with the 'wdl' extra to include this test.")(
703
+ test_item
704
+ )
632
705
  else:
633
706
  return test_item
634
707
 
@@ -637,40 +710,48 @@ def needs_server(test_item: MT) -> MT:
637
710
  """
638
711
  Use as a decorator before test classes or methods to only run them if Connexion is installed.
639
712
  """
640
- test_item = _mark_test('server_mode', test_item)
713
+ test_item = _mark_test("server_mode", test_item)
641
714
  try:
642
715
  # noinspection PyUnresolvedReferences
643
716
  import connexion
717
+
644
718
  print(connexion.__file__) # keep this import from being removed.
645
719
  except ImportError:
646
720
  return unittest.skip(
647
- "Install Toil with the 'server' extra to include this test.")(test_item)
721
+ "Install Toil with the 'server' extra to include this test."
722
+ )(test_item)
648
723
  else:
649
724
  return test_item
650
725
 
726
+
651
727
  def needs_celery_broker(test_item: MT) -> MT:
652
728
  """
653
729
  Use as a decorator before test classes or methods to run only if RabbitMQ is set up to take Celery jobs.
654
730
  """
655
- test_item = _mark_test('celery', needs_online(test_item))
656
- test_item = needs_env_var('TOIL_WES_BROKER_URL', "a URL to a RabbitMQ broker for Celery")(test_item)
731
+ test_item = _mark_test("celery", needs_online(test_item))
732
+ test_item = needs_env_var(
733
+ "TOIL_WES_BROKER_URL", "a URL to a RabbitMQ broker for Celery"
734
+ )(test_item)
657
735
  return test_item
658
736
 
737
+
659
738
  def needs_wes_server(test_item: MT) -> MT:
660
739
  """
661
740
  Use as a decorator before test classes or methods to run only if a WES
662
741
  server is available to run against.
663
742
  """
664
- test_item = _mark_test('wes_server', needs_online(test_item))
743
+ test_item = _mark_test("wes_server", needs_online(test_item))
665
744
 
666
- wes_url = os.environ.get('TOIL_WES_ENDPOINT')
745
+ wes_url = os.environ.get("TOIL_WES_ENDPOINT")
667
746
  if not wes_url:
668
747
  return unittest.skip(f"Set TOIL_WES_ENDPOINT to include this test")(test_item)
669
748
 
670
749
  try:
671
750
  urlopen(f"{wes_url}/ga4gh/wes/v1/service-info")
672
751
  except (HTTPError, URLError) as e:
673
- return unittest.skip(f"Run a WES server on {wes_url} to include this test")(test_item)
752
+ return unittest.skip(f"Run a WES server on {wes_url} to include this test")(
753
+ test_item
754
+ )
674
755
 
675
756
  return test_item
676
757
 
@@ -680,10 +761,10 @@ def needs_local_appliance(test_item: MT) -> MT:
680
761
  Use as a decorator before test classes or methods to only run them if
681
762
  the Toil appliance Docker image is downloaded.
682
763
  """
683
- test_item = _mark_test('appliance', test_item)
684
- if os.getenv('TOIL_SKIP_DOCKER', '').lower() == 'true':
685
- return unittest.skip('Skipping docker test.')(test_item)
686
- if not which('docker'):
764
+ test_item = _mark_test("appliance", test_item)
765
+ if os.getenv("TOIL_SKIP_DOCKER", "").lower() == "true":
766
+ return unittest.skip("Skipping docker test.")(test_item)
767
+ if not which("docker"):
687
768
  return unittest.skip("Install docker to include this test.")(test_item)
688
769
 
689
770
  try:
@@ -719,9 +800,9 @@ def needs_fetchable_appliance(test_item: MT) -> MT:
719
800
  the Toil appliance Docker image is able to be downloaded from the Internet.
720
801
  """
721
802
 
722
- test_item = _mark_test('fetchable_appliance', needs_online(test_item))
723
- if os.getenv('TOIL_SKIP_DOCKER', '').lower() == 'true':
724
- return unittest.skip('Skipping docker test.')(test_item)
803
+ test_item = _mark_test("fetchable_appliance", needs_online(test_item))
804
+ if os.getenv("TOIL_SKIP_DOCKER", "").lower() == "true":
805
+ return unittest.skip("Skipping docker test.")(test_item)
725
806
  try:
726
807
  applianceSelf()
727
808
  except ApplianceImageNotFound:
@@ -742,12 +823,12 @@ def integrative(test_item: MT) -> MT:
742
823
  We define integration tests as A) involving other, non-Toil software components
743
824
  that we develop and/or B) having a higher cost (time or money).
744
825
  """
745
- test_item = _mark_test('integrative', test_item)
746
- if os.getenv('TOIL_TEST_INTEGRATIVE', '').lower() == 'true':
826
+ test_item = _mark_test("integrative", test_item)
827
+ if os.getenv("TOIL_TEST_INTEGRATIVE", "").lower() == "true":
747
828
  return test_item
748
829
  else:
749
830
  return unittest.skip(
750
- 'Set TOIL_TEST_INTEGRATIVE="True" to include this integration test, '
831
+ "Set TOIL_TEST_INTEGRATIVE=True to include this integration test, "
751
832
  "or run `make integration_test_local` to run all integration tests."
752
833
  )(test_item)
753
834
 
@@ -757,14 +838,14 @@ def slow(test_item: MT) -> MT:
757
838
  Use this decorator to identify tests that are slow and not critical.
758
839
  Skip if TOIL_TEST_QUICK is true.
759
840
  """
760
- test_item = _mark_test('slow', test_item)
761
- if os.environ.get('TOIL_TEST_QUICK', '').lower() != 'true':
841
+ test_item = _mark_test("slow", test_item)
842
+ if os.environ.get("TOIL_TEST_QUICK", "").lower() != "true":
762
843
  return test_item
763
844
  else:
764
845
  return unittest.skip('Skipped because TOIL_TEST_QUICK is "True"')(test_item)
765
846
 
766
847
 
767
- methodNamePartRegex = re.compile('^[a-zA-Z_0-9]+$')
848
+ methodNamePartRegex = re.compile("^[a-zA-Z_0-9]+$")
768
849
 
769
850
 
770
851
  @contextmanager
@@ -776,7 +857,7 @@ def timeLimit(seconds: int) -> Generator[None, None, None]:
776
857
  specified amount of time. See <http://stackoverflow.com/a/601168>.
777
858
 
778
859
  :param seconds: maximum allowable time, in seconds
779
-
860
+
780
861
  >>> import time
781
862
  >>> with timeLimit(2):
782
863
  ... time.sleep(1)
@@ -787,9 +868,10 @@ def timeLimit(seconds: int) -> Generator[None, None, None]:
787
868
  ...
788
869
  RuntimeError: Timed out
789
870
  """
871
+
790
872
  # noinspection PyUnusedLocal
791
873
  def signal_handler(signum: int, frame: Any) -> None:
792
- raise RuntimeError('Timed out')
874
+ raise RuntimeError("Timed out")
793
875
 
794
876
  signal.signal(signal.SIGALRM, signal_handler)
795
877
  signal.alarm(seconds)
@@ -854,6 +936,7 @@ def make_tests(generalMethod, targetClass, **kwargs):
854
936
  False
855
937
 
856
938
  """
939
+
857
940
  def permuteIntoLeft(left, rParamName, right):
858
941
  """
859
942
  Permutes values in right dictionary into each parameter: value dict pair in the left
@@ -879,9 +962,11 @@ def make_tests(generalMethod, targetClass, **kwargs):
879
962
  """
880
963
  for prmValName, lDict in list(left.items()):
881
964
  for rValName, rVal in list(right.items()):
882
- nextPrmVal = (f'__{rParamName}_{rValName.lower()}')
965
+ nextPrmVal = f"__{rParamName}_{rValName.lower()}"
883
966
  if methodNamePartRegex.match(nextPrmVal) is None:
884
- raise RuntimeError("The name '%s' cannot be used in a method name" % pvName)
967
+ raise RuntimeError(
968
+ "The name '%s' cannot be used in a method name" % pvName
969
+ )
885
970
  aggDict = dict(lDict)
886
971
  aggDict[rParamName] = rVal
887
972
  left[prmValName + nextPrmVal] = aggDict
@@ -896,7 +981,7 @@ def make_tests(generalMethod, targetClass, **kwargs):
896
981
  else:
897
982
  return generalMethod(self)
898
983
 
899
- methodName = f'test_{generalMethod.__name__}{prmNames}'
984
+ methodName = f"test_{generalMethod.__name__}{prmNames}"
900
985
 
901
986
  setattr(targetClass, methodName, fx)
902
987
 
@@ -909,9 +994,11 @@ def make_tests(generalMethod, targetClass, **kwargs):
909
994
  left = {}
910
995
  prmName, vals = sortedKwargs.pop()
911
996
  for valName, val in list(vals.items()):
912
- pvName = f'__{prmName}_{valName.lower()}'
997
+ pvName = f"__{prmName}_{valName.lower()}"
913
998
  if methodNamePartRegex.match(pvName) is None:
914
- raise RuntimeError("The name '%s' cannot be used in a method name" % pvName)
999
+ raise RuntimeError(
1000
+ "The name '%s' cannot be used in a method name" % pvName
1001
+ )
915
1002
  left[pvName] = {prmName: val}
916
1003
 
917
1004
  # get cartesian product
@@ -937,9 +1024,9 @@ class ApplianceTestSupport(ToilTest):
937
1024
 
938
1025
  @contextmanager
939
1026
  def _applianceCluster(
940
- self, mounts: Dict[str, str], numCores: Optional[int] = None
1027
+ self, mounts: dict[str, str], numCores: Optional[int] = None
941
1028
  ) -> Generator[
942
- Tuple["ApplianceTestSupport.LeaderThread", "ApplianceTestSupport.WorkerThread"],
1029
+ tuple["ApplianceTestSupport.LeaderThread", "ApplianceTestSupport.WorkerThread"],
943
1030
  None,
944
1031
  None,
945
1032
  ]:
@@ -969,10 +1056,10 @@ class ApplianceTestSupport(ToilTest):
969
1056
  class Appliance(ExceptionalThread, metaclass=ABCMeta):
970
1057
  @abstractmethod
971
1058
  def _getRole(self) -> str:
972
- return 'leader'
1059
+ return "leader"
973
1060
 
974
1061
  @abstractmethod
975
- def _containerCommand(self) -> List[str]:
1062
+ def _containerCommand(self) -> list[str]:
976
1063
  pass
977
1064
 
978
1065
  @abstractmethod
@@ -985,7 +1072,7 @@ class ApplianceTestSupport(ToilTest):
985
1072
  def __init__(
986
1073
  self,
987
1074
  outer: "ApplianceTestSupport",
988
- mounts: Dict[str, str],
1075
+ mounts: dict[str, str],
989
1076
  cleanMounts: bool = False,
990
1077
  ) -> None:
991
1078
  assert all(
@@ -1002,15 +1089,20 @@ class ApplianceTestSupport(ToilTest):
1002
1089
  with self.lock:
1003
1090
  image = applianceSelf()
1004
1091
  # Omitting --rm, it's unreliable, see https://github.com/docker/docker/issues/16575
1005
- args = list(concat('docker', 'run',
1006
- '--entrypoint=' + self._entryPoint(),
1007
- '--net=host',
1008
- '-i',
1009
- '--name=' + self.containerName,
1010
- ['--volume=%s:%s' % mount for mount in self.mounts.items()],
1011
- image,
1012
- self._containerCommand()))
1013
- logger.info('Running %r', args)
1092
+ args = list(
1093
+ concat(
1094
+ "docker",
1095
+ "run",
1096
+ "--entrypoint=" + self._entryPoint(),
1097
+ "--net=host",
1098
+ "-i",
1099
+ "--name=" + self.containerName,
1100
+ ["--volume=%s:%s" % mount for mount in self.mounts.items()],
1101
+ image,
1102
+ self._containerCommand(),
1103
+ )
1104
+ )
1105
+ logger.info("Running %r", args)
1014
1106
  self.popen = subprocess.Popen(args)
1015
1107
  self.start()
1016
1108
  self.__wait_running()
@@ -1018,17 +1110,17 @@ class ApplianceTestSupport(ToilTest):
1018
1110
 
1019
1111
  # noinspection PyUnusedLocal
1020
1112
  def __exit__(
1021
- self, exc_type: Type[BaseException], exc_val: Exception, exc_tb: Any
1113
+ self, exc_type: type[BaseException], exc_val: Exception, exc_tb: Any
1022
1114
  ) -> Literal[False]:
1023
1115
  try:
1024
1116
  try:
1025
- self.outer._run('docker', 'stop', self.containerName)
1117
+ self.outer._run("docker", "stop", self.containerName)
1026
1118
  self.join()
1027
1119
  finally:
1028
1120
  if self.cleanMounts:
1029
1121
  self.__cleanMounts()
1030
1122
  finally:
1031
- self.outer._run('docker', 'rm', '-f', self.containerName)
1123
+ self.outer._run("docker", "rm", "-f", self.containerName)
1032
1124
  return False # don't swallow exception
1033
1125
 
1034
1126
  def __wait_running(self) -> None:
@@ -1055,7 +1147,7 @@ class ApplianceTestSupport(ToilTest):
1055
1147
  except subprocess.CalledProcessError:
1056
1148
  pass
1057
1149
  else:
1058
- if 'true' == running:
1150
+ if "true" == running:
1059
1151
  break
1060
1152
  time.sleep(1)
1061
1153
 
@@ -1068,30 +1160,33 @@ class ApplianceTestSupport(ToilTest):
1068
1160
  was stopped, otherwise the running container might still be writing files.
1069
1161
  """
1070
1162
  # Delete all files within each mounted directory, but not the directory itself.
1071
- cmd = 'shopt -s dotglob && rm -rf ' + ' '.join(v + '/*'
1072
- for k, v in self.mounts.items()
1073
- if os.path.isdir(k))
1074
- self.outer._run('docker', 'run',
1075
- '--rm',
1076
- '--entrypoint=/bin/bash',
1077
- applianceSelf(),
1078
- '-c',
1079
- cmd)
1163
+ cmd = "shopt -s dotglob && rm -rf " + " ".join(
1164
+ v + "/*" for k, v in self.mounts.items() if os.path.isdir(k)
1165
+ )
1166
+ self.outer._run(
1167
+ "docker",
1168
+ "run",
1169
+ "--rm",
1170
+ "--entrypoint=/bin/bash",
1171
+ applianceSelf(),
1172
+ "-c",
1173
+ cmd,
1174
+ )
1080
1175
 
1081
1176
  def tryRun(self) -> None:
1082
1177
  assert self.popen
1083
1178
  self.popen.wait()
1084
- logger.info('Exiting %s', self.__class__.__name__)
1179
+ logger.info("Exiting %s", self.__class__.__name__)
1085
1180
 
1086
1181
  def runOnAppliance(self, *args: str, **kwargs: Any) -> None:
1087
1182
  # Check if thread is still alive. Note that ExceptionalThread.join raises the
1088
1183
  # exception that occurred in the thread.
1089
1184
  self.join(timeout=0)
1090
1185
  # noinspection PyProtectedMember
1091
- self.outer._run('docker', 'exec', '-i', self.containerName, *args, **kwargs)
1186
+ self.outer._run("docker", "exec", "-i", self.containerName, *args, **kwargs)
1092
1187
 
1093
1188
  def writeToAppliance(self, path: str, contents: Any) -> None:
1094
- self.runOnAppliance('tee', path, input=contents)
1189
+ self.runOnAppliance("tee", path, input=contents)
1095
1190
 
1096
1191
  def deployScript(
1097
1192
  self, path: str, packagePath: str, script: Union[str, Callable[..., Any]]
@@ -1114,42 +1209,46 @@ class ApplianceTestSupport(ToilTest):
1114
1209
  packagePath_list = packagePath.split(".")
1115
1210
  packages, module = packagePath_list[:-1], packagePath_list[-1]
1116
1211
  for package in packages:
1117
- path += '/' + package
1118
- self.runOnAppliance('mkdir', '-p', path)
1119
- self.writeToAppliance(path + '/__init__.py', '')
1120
- self.writeToAppliance(path + '/' + module + '.py', script)
1212
+ path += "/" + package
1213
+ self.runOnAppliance("mkdir", "-p", path)
1214
+ self.writeToAppliance(path + "/__init__.py", "")
1215
+ self.writeToAppliance(path + "/" + module + ".py", script)
1121
1216
 
1122
1217
  class LeaderThread(Appliance):
1123
1218
  def _entryPoint(self) -> str:
1124
- return 'mesos-master'
1219
+ return "mesos-master"
1125
1220
 
1126
1221
  def _getRole(self) -> str:
1127
- return 'leader'
1222
+ return "leader"
1128
1223
 
1129
- def _containerCommand(self) -> List[str]:
1130
- return ['--registry=in_memory',
1131
- '--ip=127.0.0.1',
1132
- '--port=5050',
1133
- '--allocation_interval=500ms']
1224
+ def _containerCommand(self) -> list[str]:
1225
+ return [
1226
+ "--registry=in_memory",
1227
+ "--ip=127.0.0.1",
1228
+ "--port=5050",
1229
+ "--allocation_interval=500ms",
1230
+ ]
1134
1231
 
1135
1232
  class WorkerThread(Appliance):
1136
1233
  def __init__(
1137
- self, outer: "ApplianceTestSupport", mounts: Dict[str, str], numCores: int
1234
+ self, outer: "ApplianceTestSupport", mounts: dict[str, str], numCores: int
1138
1235
  ) -> None:
1139
1236
  self.numCores = numCores
1140
1237
  super().__init__(outer, mounts)
1141
1238
 
1142
1239
  def _entryPoint(self) -> str:
1143
- return 'mesos-agent'
1240
+ return "mesos-agent"
1144
1241
 
1145
1242
  def _getRole(self) -> str:
1146
- return 'worker'
1147
-
1148
- def _containerCommand(self) -> List[str]:
1149
- return ['--work_dir=/var/lib/mesos',
1150
- '--ip=127.0.0.1',
1151
- '--master=127.0.0.1:5050',
1152
- '--attributes=preemptible:False',
1153
- '--resources=cpus(*):%i' % self.numCores,
1154
- '--no-hostname_lookup',
1155
- '--no-systemd_enable_support']
1243
+ return "worker"
1244
+
1245
+ def _containerCommand(self) -> list[str]:
1246
+ return [
1247
+ "--work_dir=/var/lib/mesos",
1248
+ "--ip=127.0.0.1",
1249
+ "--master=127.0.0.1:5050",
1250
+ "--attributes=preemptible:False",
1251
+ "--resources=cpus(*):%i" % self.numCores,
1252
+ "--no-hostname_lookup",
1253
+ "--no-systemd_enable_support",
1254
+ ]