toil 7.0.0__py3-none-any.whl → 8.1.0b1__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 (197) hide show
  1. toil/__init__.py +124 -86
  2. toil/batchSystems/__init__.py +1 -0
  3. toil/batchSystems/abstractBatchSystem.py +137 -77
  4. toil/batchSystems/abstractGridEngineBatchSystem.py +211 -101
  5. toil/batchSystems/awsBatch.py +237 -128
  6. toil/batchSystems/cleanup_support.py +22 -16
  7. toil/batchSystems/contained_executor.py +30 -26
  8. toil/batchSystems/gridengine.py +85 -49
  9. toil/batchSystems/htcondor.py +164 -87
  10. toil/batchSystems/kubernetes.py +622 -386
  11. toil/batchSystems/local_support.py +17 -12
  12. toil/batchSystems/lsf.py +132 -79
  13. toil/batchSystems/lsfHelper.py +13 -11
  14. toil/batchSystems/mesos/__init__.py +41 -29
  15. toil/batchSystems/mesos/batchSystem.py +288 -149
  16. toil/batchSystems/mesos/executor.py +77 -49
  17. toil/batchSystems/mesos/test/__init__.py +31 -23
  18. toil/batchSystems/options.py +39 -29
  19. toil/batchSystems/registry.py +53 -19
  20. toil/batchSystems/singleMachine.py +293 -123
  21. toil/batchSystems/slurm.py +651 -155
  22. toil/batchSystems/torque.py +46 -32
  23. toil/bus.py +141 -73
  24. toil/common.py +784 -397
  25. toil/cwl/__init__.py +1 -1
  26. toil/cwl/cwltoil.py +1137 -534
  27. toil/cwl/utils.py +17 -22
  28. toil/deferred.py +62 -41
  29. toil/exceptions.py +5 -3
  30. toil/fileStores/__init__.py +5 -5
  31. toil/fileStores/abstractFileStore.py +88 -57
  32. toil/fileStores/cachingFileStore.py +711 -247
  33. toil/fileStores/nonCachingFileStore.py +113 -75
  34. toil/job.py +1031 -349
  35. toil/jobStores/abstractJobStore.py +387 -243
  36. toil/jobStores/aws/jobStore.py +772 -412
  37. toil/jobStores/aws/utils.py +161 -109
  38. toil/jobStores/conftest.py +1 -0
  39. toil/jobStores/fileJobStore.py +289 -151
  40. toil/jobStores/googleJobStore.py +137 -70
  41. toil/jobStores/utils.py +36 -15
  42. toil/leader.py +614 -269
  43. toil/lib/accelerators.py +115 -18
  44. toil/lib/aws/__init__.py +55 -28
  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 +204 -58
  49. toil/lib/aws/utils.py +290 -213
  50. toil/lib/bioio.py +13 -5
  51. toil/lib/compatibility.py +11 -6
  52. toil/lib/conversions.py +83 -49
  53. toil/lib/docker.py +131 -103
  54. toil/lib/dockstore.py +379 -0
  55. toil/lib/ec2.py +322 -209
  56. toil/lib/ec2nodes.py +174 -105
  57. toil/lib/encryption/_dummy.py +5 -3
  58. toil/lib/encryption/_nacl.py +10 -6
  59. toil/lib/encryption/conftest.py +1 -0
  60. toil/lib/exceptions.py +26 -7
  61. toil/lib/expando.py +4 -2
  62. toil/lib/ftp_utils.py +217 -0
  63. toil/lib/generatedEC2Lists.py +127 -19
  64. toil/lib/history.py +1271 -0
  65. toil/lib/history_submission.py +681 -0
  66. toil/lib/humanize.py +6 -2
  67. toil/lib/io.py +121 -12
  68. toil/lib/iterables.py +4 -2
  69. toil/lib/memoize.py +12 -8
  70. toil/lib/misc.py +83 -18
  71. toil/lib/objects.py +2 -2
  72. toil/lib/resources.py +19 -7
  73. toil/lib/retry.py +125 -87
  74. toil/lib/threading.py +282 -80
  75. toil/lib/throttle.py +15 -14
  76. toil/lib/trs.py +390 -0
  77. toil/lib/web.py +38 -0
  78. toil/options/common.py +850 -402
  79. toil/options/cwl.py +185 -90
  80. toil/options/runner.py +50 -0
  81. toil/options/wdl.py +70 -19
  82. toil/provisioners/__init__.py +111 -46
  83. toil/provisioners/abstractProvisioner.py +322 -157
  84. toil/provisioners/aws/__init__.py +62 -30
  85. toil/provisioners/aws/awsProvisioner.py +980 -627
  86. toil/provisioners/clusterScaler.py +541 -279
  87. toil/provisioners/gceProvisioner.py +283 -180
  88. toil/provisioners/node.py +147 -79
  89. toil/realtimeLogger.py +34 -22
  90. toil/resource.py +137 -75
  91. toil/server/app.py +127 -61
  92. toil/server/celery_app.py +3 -1
  93. toil/server/cli/wes_cwl_runner.py +84 -55
  94. toil/server/utils.py +56 -31
  95. toil/server/wes/abstract_backend.py +64 -26
  96. toil/server/wes/amazon_wes_utils.py +21 -15
  97. toil/server/wes/tasks.py +121 -63
  98. toil/server/wes/toil_backend.py +142 -107
  99. toil/server/wsgi_app.py +4 -3
  100. toil/serviceManager.py +58 -22
  101. toil/statsAndLogging.py +183 -65
  102. toil/test/__init__.py +263 -179
  103. toil/test/batchSystems/batchSystemTest.py +438 -195
  104. toil/test/batchSystems/batch_system_plugin_test.py +18 -7
  105. toil/test/batchSystems/test_gridengine.py +173 -0
  106. toil/test/batchSystems/test_lsf_helper.py +67 -58
  107. toil/test/batchSystems/test_slurm.py +265 -49
  108. toil/test/cactus/test_cactus_integration.py +20 -22
  109. toil/test/cwl/conftest.py +39 -0
  110. toil/test/cwl/cwlTest.py +375 -72
  111. toil/test/cwl/measure_default_memory.cwl +12 -0
  112. toil/test/cwl/not_run_required_input.cwl +29 -0
  113. toil/test/cwl/optional-file.cwl +18 -0
  114. toil/test/cwl/scatter_duplicate_outputs.cwl +40 -0
  115. toil/test/docs/scriptsTest.py +60 -34
  116. toil/test/jobStores/jobStoreTest.py +412 -235
  117. toil/test/lib/aws/test_iam.py +116 -48
  118. toil/test/lib/aws/test_s3.py +16 -9
  119. toil/test/lib/aws/test_utils.py +5 -6
  120. toil/test/lib/dockerTest.py +118 -141
  121. toil/test/lib/test_conversions.py +113 -115
  122. toil/test/lib/test_ec2.py +57 -49
  123. toil/test/lib/test_history.py +212 -0
  124. toil/test/lib/test_misc.py +12 -5
  125. toil/test/lib/test_trs.py +161 -0
  126. toil/test/mesos/MesosDataStructuresTest.py +23 -10
  127. toil/test/mesos/helloWorld.py +7 -6
  128. toil/test/mesos/stress.py +25 -20
  129. toil/test/options/options.py +7 -2
  130. toil/test/provisioners/aws/awsProvisionerTest.py +293 -140
  131. toil/test/provisioners/clusterScalerTest.py +440 -250
  132. toil/test/provisioners/clusterTest.py +81 -42
  133. toil/test/provisioners/gceProvisionerTest.py +174 -100
  134. toil/test/provisioners/provisionerTest.py +25 -13
  135. toil/test/provisioners/restartScript.py +5 -4
  136. toil/test/server/serverTest.py +188 -141
  137. toil/test/sort/restart_sort.py +137 -68
  138. toil/test/sort/sort.py +134 -66
  139. toil/test/sort/sortTest.py +91 -49
  140. toil/test/src/autoDeploymentTest.py +140 -100
  141. toil/test/src/busTest.py +20 -18
  142. toil/test/src/checkpointTest.py +8 -2
  143. toil/test/src/deferredFunctionTest.py +49 -35
  144. toil/test/src/dockerCheckTest.py +33 -26
  145. toil/test/src/environmentTest.py +20 -10
  146. toil/test/src/fileStoreTest.py +538 -271
  147. toil/test/src/helloWorldTest.py +7 -4
  148. toil/test/src/importExportFileTest.py +61 -31
  149. toil/test/src/jobDescriptionTest.py +32 -17
  150. toil/test/src/jobEncapsulationTest.py +2 -0
  151. toil/test/src/jobFileStoreTest.py +74 -50
  152. toil/test/src/jobServiceTest.py +187 -73
  153. toil/test/src/jobTest.py +120 -70
  154. toil/test/src/miscTests.py +19 -18
  155. toil/test/src/promisedRequirementTest.py +82 -36
  156. toil/test/src/promisesTest.py +7 -6
  157. toil/test/src/realtimeLoggerTest.py +6 -6
  158. toil/test/src/regularLogTest.py +71 -37
  159. toil/test/src/resourceTest.py +80 -49
  160. toil/test/src/restartDAGTest.py +36 -22
  161. toil/test/src/resumabilityTest.py +9 -2
  162. toil/test/src/retainTempDirTest.py +45 -14
  163. toil/test/src/systemTest.py +12 -8
  164. toil/test/src/threadingTest.py +44 -25
  165. toil/test/src/toilContextManagerTest.py +10 -7
  166. toil/test/src/userDefinedJobArgTypeTest.py +8 -5
  167. toil/test/src/workerTest.py +33 -16
  168. toil/test/utils/toilDebugTest.py +70 -58
  169. toil/test/utils/toilKillTest.py +4 -5
  170. toil/test/utils/utilsTest.py +239 -102
  171. toil/test/wdl/wdltoil_test.py +789 -148
  172. toil/test/wdl/wdltoil_test_kubernetes.py +37 -23
  173. toil/toilState.py +52 -26
  174. toil/utils/toilConfig.py +13 -4
  175. toil/utils/toilDebugFile.py +44 -27
  176. toil/utils/toilDebugJob.py +85 -25
  177. toil/utils/toilDestroyCluster.py +11 -6
  178. toil/utils/toilKill.py +8 -3
  179. toil/utils/toilLaunchCluster.py +251 -145
  180. toil/utils/toilMain.py +37 -16
  181. toil/utils/toilRsyncCluster.py +27 -14
  182. toil/utils/toilSshCluster.py +45 -22
  183. toil/utils/toilStats.py +75 -36
  184. toil/utils/toilStatus.py +226 -119
  185. toil/utils/toilUpdateEC2Instances.py +3 -1
  186. toil/version.py +6 -6
  187. toil/wdl/utils.py +5 -5
  188. toil/wdl/wdltoil.py +3528 -1053
  189. toil/worker.py +370 -149
  190. toil-8.1.0b1.dist-info/METADATA +178 -0
  191. toil-8.1.0b1.dist-info/RECORD +259 -0
  192. {toil-7.0.0.dist-info → toil-8.1.0b1.dist-info}/WHEEL +1 -1
  193. toil-7.0.0.dist-info/METADATA +0 -158
  194. toil-7.0.0.dist-info/RECORD +0 -244
  195. {toil-7.0.0.dist-info → toil-8.1.0b1.dist-info}/LICENSE +0 -0
  196. {toil-7.0.0.dist-info → toil-8.1.0b1.dist-info}/entry_points.txt +0 -0
  197. {toil-7.0.0.dist-info → toil-8.1.0b1.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,42 +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
- Literal,
40
- Optional,
41
- Tuple,
42
- Type,
43
- TypeVar,
44
- Union,
45
- cast)
36
+ from typing import Any, Callable, Literal, Optional, TypeVar, Union, cast
46
37
  from unittest.util import strclass
47
38
  from urllib.error import HTTPError, URLError
48
39
  from urllib.request import urlopen
49
40
 
50
-
51
- if sys.version_info >= (3, 9):
52
- import zoneinfo
53
- else:
54
- from backports import zoneinfo
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)
42
+ from toil.lib.accelerators import (
43
+ have_working_nvidia_docker_runtime,
44
+ have_working_nvidia_smi,
45
+ )
59
46
  from toil.lib.io import mkdtemp
60
47
  from toil.lib.iterables import concat
61
48
  from toil.lib.memoize import memoize
@@ -82,20 +69,24 @@ class ToilTest(unittest.TestCase):
82
69
  """
83
70
 
84
71
  _tempBaseDir: Optional[str] = None
85
- _tempDirs: List[str] = []
72
+ _tempDirs: list[str] = []
86
73
 
87
74
  def setup_method(self, method: Any) -> None:
88
75
  western = zoneinfo.ZoneInfo("America/Los_Angeles")
89
76
  california_time = datetime.datetime.now(tz=western)
90
77
  timestamp = california_time.strftime("%b %d %Y %H:%M:%S:%f %Z")
91
- 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
+ )
92
81
 
93
82
  @classmethod
94
83
  def setUpClass(cls) -> None:
95
84
  super().setUpClass()
96
- tempBaseDir = os.environ.get('TOIL_TEST_TEMP', None)
85
+ tempBaseDir = os.environ.get("TOIL_TEST_TEMP", None)
97
86
  if tempBaseDir is not None and not os.path.isabs(tempBaseDir):
98
- tempBaseDir = os.path.abspath(os.path.join(cls._projectRootPath(), tempBaseDir))
87
+ tempBaseDir = os.path.abspath(
88
+ os.path.join(cls._projectRootPath(), tempBaseDir)
89
+ )
99
90
  os.makedirs(tempBaseDir, exist_ok=True)
100
91
  cls._tempBaseDir = tempBaseDir
101
92
 
@@ -127,7 +118,8 @@ class ToilTest(unittest.TestCase):
127
118
  the instance is located
128
119
  """
129
120
  from toil.lib.aws import running_on_ec2
130
- return cls._region() if running_on_ec2() else 'us-west-2'
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,32 +349,34 @@ 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
378
  from boto3 import Session
379
+
376
380
  session = Session()
377
381
  boto3_credentials = session.get_credentials()
378
382
  except ImportError:
@@ -380,18 +384,25 @@ def needs_aws_s3(test_item: MT) -> MT:
380
384
  test_item
381
385
  )
382
386
  from toil.lib.aws import running_on_ec2
383
- if not (boto3_credentials or os.path.exists(os.path.expanduser('~/.aws/credentials')) or running_on_ec2()):
384
- return unittest.skip("Configure AWS credentials to include this test.")(test_item)
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
+ )
385
396
  return test_item
386
397
 
387
398
 
388
399
  def needs_aws_ec2(test_item: MT) -> MT:
389
400
  """Use as a decorator before test classes or methods to run only if AWS EC2 is usable."""
390
401
  # Assume we need S3 as well as EC2
391
- test_item = _mark_test('aws-ec2', needs_aws_s3(test_item))
402
+ test_item = _mark_test("aws-ec2", needs_aws_s3(test_item))
392
403
  # In addition to S3 we also need an SSH key to deploy with.
393
404
  # TODO: We assume that if this is set we have EC2 access.
394
- 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)
395
406
  return test_item
396
407
 
397
408
 
@@ -401,16 +412,23 @@ def needs_aws_batch(test_item: MT) -> MT:
401
412
  is usable.
402
413
  """
403
414
  # Assume we need S3 as well as Batch
404
- test_item = _mark_test('aws-batch', needs_aws_s3(test_item))
415
+ test_item = _mark_test("aws-batch", needs_aws_s3(test_item))
405
416
  # Assume we have Batch if the user has set these variables.
406
- test_item = needs_env_var('TOIL_AWS_BATCH_QUEUE', 'an AWS Batch queue name or ARN')(test_item)
407
- 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)
408
423
  try:
409
424
  from toil.lib.aws import get_current_aws_region
425
+
410
426
  if get_current_aws_region() is None:
411
427
  # We don't know a region so we need one set.
412
428
  # TODO: It always won't be set if we get here.
413
- 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)
414
432
 
415
433
  except ImportError:
416
434
  return unittest.skip("Install Toil with the 'aws' extra to include this test.")(
@@ -418,67 +436,79 @@ def needs_aws_batch(test_item: MT) -> MT:
418
436
  )
419
437
  return test_item
420
438
 
439
+
421
440
  def needs_google_storage(test_item: MT) -> MT:
422
441
  """
423
442
  Use as a decorator before test classes or methods to run only if Google
424
443
  Cloud is installed and we ought to be able to access public Google Storage
425
444
  URIs.
426
445
  """
427
- test_item = _mark_test('google-storage', needs_online(test_item))
446
+ test_item = _mark_test("google-storage", needs_online(test_item))
428
447
  try:
429
448
  from google.cloud import storage # noqa
430
449
  except ImportError:
431
- 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)
432
453
 
433
454
  return test_item
434
455
 
456
+
435
457
  def needs_google_project(test_item: MT) -> MT:
436
458
  """
437
459
  Use as a decorator before test classes or methods to run only if we have a Google Cloud project set.
438
460
  """
439
- test_item = _mark_test('google-project', needs_online(test_item))
440
- 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)
441
463
  return test_item
442
464
 
443
465
 
444
466
  def needs_gridengine(test_item: MT) -> MT:
445
467
  """Use as a decorator before test classes or methods to run only if GridEngine is installed."""
446
- test_item = _mark_test('gridengine', test_item)
447
- if which('qhost'):
468
+ test_item = _mark_test("gridengine", test_item)
469
+ if which("qhost"):
448
470
  return test_item
449
471
  return unittest.skip("Install GridEngine to include this test.")(test_item)
450
472
 
451
473
 
452
474
  def needs_torque(test_item: MT) -> MT:
453
475
  """Use as a decorator before test classes or methods to run only if PBS/Torque is installed."""
454
- test_item = _mark_test('torque', test_item)
455
- if which('pbsnodes'):
476
+ test_item = _mark_test("torque", test_item)
477
+ if which("pbsnodes"):
456
478
  return test_item
457
479
  return unittest.skip("Install PBS/Torque to include this test.")(test_item)
458
480
 
481
+
459
482
  def needs_kubernetes_installed(test_item: MT) -> MT:
460
483
  """Use as a decorator before test classes or methods to run only if Kubernetes is installed."""
461
- test_item = _mark_test('kubernetes', test_item)
484
+ test_item = _mark_test("kubernetes", test_item)
462
485
  try:
463
486
  import kubernetes
487
+
464
488
  str(kubernetes) # to prevent removal of this import
465
489
  except ImportError:
466
- 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)
467
493
  return test_item
468
494
 
495
+
469
496
  def needs_kubernetes(test_item: MT) -> MT:
470
497
  """Use as a decorator before test classes or methods to run only if Kubernetes is installed and configured."""
471
498
  test_item = needs_kubernetes_installed(needs_online(test_item))
472
499
  try:
473
500
  import kubernetes
501
+
474
502
  try:
475
503
  kubernetes.config.load_kube_config()
476
504
  except kubernetes.config.ConfigException:
477
505
  try:
478
506
  kubernetes.config.load_incluster_config()
479
507
  except kubernetes.config.ConfigException:
480
- return unittest.skip("Configure Kubernetes (~/.kube/config, $KUBECONFIG, "
481
- "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)
482
512
  except ImportError:
483
513
  # We should already be skipping this test
484
514
  pass
@@ -487,37 +517,50 @@ def needs_kubernetes(test_item: MT) -> MT:
487
517
 
488
518
  def needs_mesos(test_item: MT) -> MT:
489
519
  """Use as a decorator before test classes or methods to run only if Mesos is installed."""
490
- test_item = _mark_test('mesos', test_item)
491
- if not (which('mesos-master') or which('mesos-agent')):
492
- 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)
493
525
  try:
494
526
  import psutil # noqa
495
527
  import pymesos # noqa
496
528
  except ImportError:
497
- 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)
498
532
  return test_item
499
533
 
500
534
 
501
535
  def needs_slurm(test_item: MT) -> MT:
502
536
  """Use as a decorator before test classes or methods to run only if Slurm is installed."""
503
- test_item = _mark_test('slurm', test_item)
504
- if which('squeue'):
537
+ test_item = _mark_test("slurm", test_item)
538
+ if which("squeue"):
505
539
  return test_item
506
540
  return unittest.skip("Install Slurm to include this test.")(test_item)
507
541
 
508
542
 
509
543
  def needs_htcondor(test_item: MT) -> MT:
510
544
  """Use a decorator before test classes or methods to run only if the HTCondor is installed."""
511
- test_item = _mark_test('htcondor', test_item)
545
+ test_item = _mark_test("htcondor", test_item)
512
546
  try:
513
547
  import htcondor
514
- htcondor.Collector(os.getenv('TOIL_HTCONDOR_COLLECTOR')).query(constraint='False')
548
+
549
+ htcondor.Collector(os.getenv("TOIL_HTCONDOR_COLLECTOR")).query(
550
+ constraint="False"
551
+ )
515
552
  except ImportError:
516
- 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)
517
556
  except OSError:
518
- 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
+ )
519
560
  except RuntimeError:
520
- 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)
521
564
  else:
522
565
  return test_item
523
566
 
@@ -526,8 +569,8 @@ def needs_lsf(test_item: MT) -> MT:
526
569
  """
527
570
  Use as a decorator before test classes or methods to only run them if LSF is installed.
528
571
  """
529
- test_item = _mark_test('lsf', test_item)
530
- if which('bsub'):
572
+ test_item = _mark_test("lsf", test_item)
573
+ if which("bsub"):
531
574
  return test_item
532
575
  else:
533
576
  return unittest.skip("Install LSF to include this test.")(test_item)
@@ -535,8 +578,8 @@ def needs_lsf(test_item: MT) -> MT:
535
578
 
536
579
  def needs_java(test_item: MT) -> MT:
537
580
  """Use as a test decorator to run only if java is installed."""
538
- test_item = _mark_test('java', test_item)
539
- if which('java'):
581
+ test_item = _mark_test("java", test_item)
582
+ if which("java"):
540
583
  return test_item
541
584
  else:
542
585
  return unittest.skip("Install java to include this test.")(test_item)
@@ -547,74 +590,84 @@ def needs_docker(test_item: MT) -> MT:
547
590
  Use as a decorator before test classes or methods to only run them if
548
591
  docker is installed and docker-based tests are enabled.
549
592
  """
550
- test_item = _mark_test('docker', needs_online(test_item))
551
- if os.getenv('TOIL_SKIP_DOCKER', '').lower() == 'true':
552
- return unittest.skip('Skipping docker test.')(test_item)
553
- 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"):
554
597
  return test_item
555
598
  else:
556
599
  return unittest.skip("Install docker to include this test.")(test_item)
557
600
 
601
+
558
602
  def needs_singularity(test_item: MT) -> MT:
559
603
  """
560
604
  Use as a decorator before test classes or methods to only run them if
561
605
  singularity is installed.
562
606
  """
563
- test_item = _mark_test('singularity', needs_online(test_item))
564
- if which('singularity'):
607
+ test_item = _mark_test("singularity", needs_online(test_item))
608
+ if which("singularity"):
565
609
  return test_item
566
610
  else:
567
611
  return unittest.skip("Install singularity to include this test.")(test_item)
568
612
 
613
+
569
614
  def needs_singularity_or_docker(test_item: MT) -> MT:
570
615
  """
571
616
  Use as a decorator before test classes or methods to only run them if
572
617
  docker is installed and docker-based tests are enabled, or if Singularity
573
618
  is installed.
574
619
  """
575
-
620
+
576
621
  # TODO: Is there a good way to OR decorators?
577
- if which('singularity'):
622
+ if which("singularity"):
578
623
  # Singularity is here, say it's a Singularity test
579
624
  return needs_singularity(test_item)
580
625
  else:
581
626
  # Otherwise say it's a Docker test.
582
627
  return needs_docker(test_item)
583
-
628
+
629
+
584
630
  def needs_local_cuda(test_item: MT) -> MT:
585
631
  """
586
632
  Use as a decorator before test classes or methods to only run them if
587
633
  a CUDA setup legible to cwltool (i.e. providing userspace nvidia-smi) is present.
588
634
  """
589
- test_item = _mark_test('local_cuda', test_item)
635
+ test_item = _mark_test("local_cuda", test_item)
590
636
  if have_working_nvidia_smi():
591
637
  return test_item
592
638
  else:
593
- 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
+
594
643
 
595
644
  def needs_docker_cuda(test_item: MT) -> MT:
596
645
  """
597
646
  Use as a decorator before test classes or methods to only run them if
598
647
  a CUDA setup is available through Docker.
599
648
  """
600
- test_item = _mark_test('docker_cuda', needs_online(test_item))
649
+ test_item = _mark_test("docker_cuda", needs_online(test_item))
601
650
  if have_working_nvidia_docker_runtime():
602
651
  return test_item
603
652
  else:
604
- 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
+
605
657
 
606
658
  def needs_encryption(test_item: MT) -> MT:
607
659
  """
608
660
  Use as a decorator before test classes or methods to only run them if PyNaCl is installed
609
661
  and configured.
610
662
  """
611
- test_item = _mark_test('encryption', test_item)
663
+ test_item = _mark_test("encryption", test_item)
612
664
  try:
613
665
  # noinspection PyUnresolvedReferences
614
666
  import nacl # noqa
615
667
  except ImportError:
616
668
  return unittest.skip(
617
- "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)
618
671
  else:
619
672
  return test_item
620
673
 
@@ -624,26 +677,31 @@ def needs_cwl(test_item: MT) -> MT:
624
677
  Use as a decorator before test classes or methods to only run them if CWLTool is installed
625
678
  and configured.
626
679
  """
627
- test_item = _mark_test('cwl', test_item)
680
+ test_item = _mark_test("cwl", test_item)
628
681
  try:
629
682
  # noinspection PyUnresolvedReferences
630
683
  import cwltool # noqa
631
684
  except ImportError:
632
- 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
+ )
633
688
  else:
634
689
  return test_item
635
690
 
691
+
636
692
  def needs_wdl(test_item: MT) -> MT:
637
693
  """
638
694
  Use as a decorator before test classes or methods to only run them if miniwdl is installed
639
695
  and configured.
640
696
  """
641
- test_item = _mark_test('wdl', test_item)
697
+ test_item = _mark_test("wdl", test_item)
642
698
  try:
643
699
  # noinspection PyUnresolvedReferences
644
700
  import WDL # noqa
645
701
  except ImportError:
646
- return unittest.skip("Install Toil with the 'wdl' extra to include this test.")(test_item)
702
+ return unittest.skip("Install Toil with the 'wdl' extra to include this test.")(
703
+ test_item
704
+ )
647
705
  else:
648
706
  return test_item
649
707
 
@@ -652,40 +710,48 @@ def needs_server(test_item: MT) -> MT:
652
710
  """
653
711
  Use as a decorator before test classes or methods to only run them if Connexion is installed.
654
712
  """
655
- test_item = _mark_test('server_mode', test_item)
713
+ test_item = _mark_test("server_mode", test_item)
656
714
  try:
657
715
  # noinspection PyUnresolvedReferences
658
716
  import connexion
717
+
659
718
  print(connexion.__file__) # keep this import from being removed.
660
719
  except ImportError:
661
720
  return unittest.skip(
662
- "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)
663
723
  else:
664
724
  return test_item
665
725
 
726
+
666
727
  def needs_celery_broker(test_item: MT) -> MT:
667
728
  """
668
729
  Use as a decorator before test classes or methods to run only if RabbitMQ is set up to take Celery jobs.
669
730
  """
670
- test_item = _mark_test('celery', needs_online(test_item))
671
- 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)
672
735
  return test_item
673
736
 
737
+
674
738
  def needs_wes_server(test_item: MT) -> MT:
675
739
  """
676
740
  Use as a decorator before test classes or methods to run only if a WES
677
741
  server is available to run against.
678
742
  """
679
- test_item = _mark_test('wes_server', needs_online(test_item))
743
+ test_item = _mark_test("wes_server", needs_online(test_item))
680
744
 
681
- wes_url = os.environ.get('TOIL_WES_ENDPOINT')
745
+ wes_url = os.environ.get("TOIL_WES_ENDPOINT")
682
746
  if not wes_url:
683
747
  return unittest.skip(f"Set TOIL_WES_ENDPOINT to include this test")(test_item)
684
748
 
685
749
  try:
686
750
  urlopen(f"{wes_url}/ga4gh/wes/v1/service-info")
687
751
  except (HTTPError, URLError) as e:
688
- 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
+ )
689
755
 
690
756
  return test_item
691
757
 
@@ -695,10 +761,10 @@ def needs_local_appliance(test_item: MT) -> MT:
695
761
  Use as a decorator before test classes or methods to only run them if
696
762
  the Toil appliance Docker image is downloaded.
697
763
  """
698
- test_item = _mark_test('appliance', test_item)
699
- if os.getenv('TOIL_SKIP_DOCKER', '').lower() == 'true':
700
- return unittest.skip('Skipping docker test.')(test_item)
701
- 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"):
702
768
  return unittest.skip("Install docker to include this test.")(test_item)
703
769
 
704
770
  try:
@@ -734,9 +800,9 @@ def needs_fetchable_appliance(test_item: MT) -> MT:
734
800
  the Toil appliance Docker image is able to be downloaded from the Internet.
735
801
  """
736
802
 
737
- test_item = _mark_test('fetchable_appliance', needs_online(test_item))
738
- if os.getenv('TOIL_SKIP_DOCKER', '').lower() == 'true':
739
- 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)
740
806
  try:
741
807
  applianceSelf()
742
808
  except ApplianceImageNotFound:
@@ -757,12 +823,12 @@ def integrative(test_item: MT) -> MT:
757
823
  We define integration tests as A) involving other, non-Toil software components
758
824
  that we develop and/or B) having a higher cost (time or money).
759
825
  """
760
- test_item = _mark_test('integrative', test_item)
761
- 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":
762
828
  return test_item
763
829
  else:
764
830
  return unittest.skip(
765
- 'Set TOIL_TEST_INTEGRATIVE=True to include this integration test, '
831
+ "Set TOIL_TEST_INTEGRATIVE=True to include this integration test, "
766
832
  "or run `make integration_test_local` to run all integration tests."
767
833
  )(test_item)
768
834
 
@@ -772,14 +838,14 @@ def slow(test_item: MT) -> MT:
772
838
  Use this decorator to identify tests that are slow and not critical.
773
839
  Skip if TOIL_TEST_QUICK is true.
774
840
  """
775
- test_item = _mark_test('slow', test_item)
776
- 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":
777
843
  return test_item
778
844
  else:
779
845
  return unittest.skip('Skipped because TOIL_TEST_QUICK is "True"')(test_item)
780
846
 
781
847
 
782
- methodNamePartRegex = re.compile('^[a-zA-Z_0-9]+$')
848
+ methodNamePartRegex = re.compile("^[a-zA-Z_0-9]+$")
783
849
 
784
850
 
785
851
  @contextmanager
@@ -791,7 +857,7 @@ def timeLimit(seconds: int) -> Generator[None, None, None]:
791
857
  specified amount of time. See <http://stackoverflow.com/a/601168>.
792
858
 
793
859
  :param seconds: maximum allowable time, in seconds
794
-
860
+
795
861
  >>> import time
796
862
  >>> with timeLimit(2):
797
863
  ... time.sleep(1)
@@ -802,9 +868,10 @@ def timeLimit(seconds: int) -> Generator[None, None, None]:
802
868
  ...
803
869
  RuntimeError: Timed out
804
870
  """
871
+
805
872
  # noinspection PyUnusedLocal
806
873
  def signal_handler(signum: int, frame: Any) -> None:
807
- raise RuntimeError('Timed out')
874
+ raise RuntimeError("Timed out")
808
875
 
809
876
  signal.signal(signal.SIGALRM, signal_handler)
810
877
  signal.alarm(seconds)
@@ -869,6 +936,7 @@ def make_tests(generalMethod, targetClass, **kwargs):
869
936
  False
870
937
 
871
938
  """
939
+
872
940
  def permuteIntoLeft(left, rParamName, right):
873
941
  """
874
942
  Permutes values in right dictionary into each parameter: value dict pair in the left
@@ -894,9 +962,11 @@ def make_tests(generalMethod, targetClass, **kwargs):
894
962
  """
895
963
  for prmValName, lDict in list(left.items()):
896
964
  for rValName, rVal in list(right.items()):
897
- nextPrmVal = (f'__{rParamName}_{rValName.lower()}')
965
+ nextPrmVal = f"__{rParamName}_{rValName.lower()}"
898
966
  if methodNamePartRegex.match(nextPrmVal) is None:
899
- 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
+ )
900
970
  aggDict = dict(lDict)
901
971
  aggDict[rParamName] = rVal
902
972
  left[prmValName + nextPrmVal] = aggDict
@@ -911,7 +981,7 @@ def make_tests(generalMethod, targetClass, **kwargs):
911
981
  else:
912
982
  return generalMethod(self)
913
983
 
914
- methodName = f'test_{generalMethod.__name__}{prmNames}'
984
+ methodName = f"test_{generalMethod.__name__}{prmNames}"
915
985
 
916
986
  setattr(targetClass, methodName, fx)
917
987
 
@@ -924,9 +994,11 @@ def make_tests(generalMethod, targetClass, **kwargs):
924
994
  left = {}
925
995
  prmName, vals = sortedKwargs.pop()
926
996
  for valName, val in list(vals.items()):
927
- pvName = f'__{prmName}_{valName.lower()}'
997
+ pvName = f"__{prmName}_{valName.lower()}"
928
998
  if methodNamePartRegex.match(pvName) is None:
929
- 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
+ )
930
1002
  left[pvName] = {prmName: val}
931
1003
 
932
1004
  # get cartesian product
@@ -952,9 +1024,9 @@ class ApplianceTestSupport(ToilTest):
952
1024
 
953
1025
  @contextmanager
954
1026
  def _applianceCluster(
955
- self, mounts: Dict[str, str], numCores: Optional[int] = None
1027
+ self, mounts: dict[str, str], numCores: Optional[int] = None
956
1028
  ) -> Generator[
957
- Tuple["ApplianceTestSupport.LeaderThread", "ApplianceTestSupport.WorkerThread"],
1029
+ tuple["ApplianceTestSupport.LeaderThread", "ApplianceTestSupport.WorkerThread"],
958
1030
  None,
959
1031
  None,
960
1032
  ]:
@@ -984,10 +1056,10 @@ class ApplianceTestSupport(ToilTest):
984
1056
  class Appliance(ExceptionalThread, metaclass=ABCMeta):
985
1057
  @abstractmethod
986
1058
  def _getRole(self) -> str:
987
- return 'leader'
1059
+ return "leader"
988
1060
 
989
1061
  @abstractmethod
990
- def _containerCommand(self) -> List[str]:
1062
+ def _containerCommand(self) -> list[str]:
991
1063
  pass
992
1064
 
993
1065
  @abstractmethod
@@ -1000,7 +1072,7 @@ class ApplianceTestSupport(ToilTest):
1000
1072
  def __init__(
1001
1073
  self,
1002
1074
  outer: "ApplianceTestSupport",
1003
- mounts: Dict[str, str],
1075
+ mounts: dict[str, str],
1004
1076
  cleanMounts: bool = False,
1005
1077
  ) -> None:
1006
1078
  assert all(
@@ -1017,15 +1089,20 @@ class ApplianceTestSupport(ToilTest):
1017
1089
  with self.lock:
1018
1090
  image = applianceSelf()
1019
1091
  # Omitting --rm, it's unreliable, see https://github.com/docker/docker/issues/16575
1020
- args = list(concat('docker', 'run',
1021
- '--entrypoint=' + self._entryPoint(),
1022
- '--net=host',
1023
- '-i',
1024
- '--name=' + self.containerName,
1025
- ['--volume=%s:%s' % mount for mount in self.mounts.items()],
1026
- image,
1027
- self._containerCommand()))
1028
- 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)
1029
1106
  self.popen = subprocess.Popen(args)
1030
1107
  self.start()
1031
1108
  self.__wait_running()
@@ -1033,17 +1110,17 @@ class ApplianceTestSupport(ToilTest):
1033
1110
 
1034
1111
  # noinspection PyUnusedLocal
1035
1112
  def __exit__(
1036
- self, exc_type: Type[BaseException], exc_val: Exception, exc_tb: Any
1113
+ self, exc_type: type[BaseException], exc_val: Exception, exc_tb: Any
1037
1114
  ) -> Literal[False]:
1038
1115
  try:
1039
1116
  try:
1040
- self.outer._run('docker', 'stop', self.containerName)
1117
+ self.outer._run("docker", "stop", self.containerName)
1041
1118
  self.join()
1042
1119
  finally:
1043
1120
  if self.cleanMounts:
1044
1121
  self.__cleanMounts()
1045
1122
  finally:
1046
- self.outer._run('docker', 'rm', '-f', self.containerName)
1123
+ self.outer._run("docker", "rm", "-f", self.containerName)
1047
1124
  return False # don't swallow exception
1048
1125
 
1049
1126
  def __wait_running(self) -> None:
@@ -1070,7 +1147,7 @@ class ApplianceTestSupport(ToilTest):
1070
1147
  except subprocess.CalledProcessError:
1071
1148
  pass
1072
1149
  else:
1073
- if 'true' == running:
1150
+ if "true" == running:
1074
1151
  break
1075
1152
  time.sleep(1)
1076
1153
 
@@ -1083,30 +1160,33 @@ class ApplianceTestSupport(ToilTest):
1083
1160
  was stopped, otherwise the running container might still be writing files.
1084
1161
  """
1085
1162
  # Delete all files within each mounted directory, but not the directory itself.
1086
- cmd = 'shopt -s dotglob && rm -rf ' + ' '.join(v + '/*'
1087
- for k, v in self.mounts.items()
1088
- if os.path.isdir(k))
1089
- self.outer._run('docker', 'run',
1090
- '--rm',
1091
- '--entrypoint=/bin/bash',
1092
- applianceSelf(),
1093
- '-c',
1094
- 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
+ )
1095
1175
 
1096
1176
  def tryRun(self) -> None:
1097
1177
  assert self.popen
1098
1178
  self.popen.wait()
1099
- logger.info('Exiting %s', self.__class__.__name__)
1179
+ logger.info("Exiting %s", self.__class__.__name__)
1100
1180
 
1101
1181
  def runOnAppliance(self, *args: str, **kwargs: Any) -> None:
1102
1182
  # Check if thread is still alive. Note that ExceptionalThread.join raises the
1103
1183
  # exception that occurred in the thread.
1104
1184
  self.join(timeout=0)
1105
1185
  # noinspection PyProtectedMember
1106
- self.outer._run('docker', 'exec', '-i', self.containerName, *args, **kwargs)
1186
+ self.outer._run("docker", "exec", "-i", self.containerName, *args, **kwargs)
1107
1187
 
1108
1188
  def writeToAppliance(self, path: str, contents: Any) -> None:
1109
- self.runOnAppliance('tee', path, input=contents)
1189
+ self.runOnAppliance("tee", path, input=contents)
1110
1190
 
1111
1191
  def deployScript(
1112
1192
  self, path: str, packagePath: str, script: Union[str, Callable[..., Any]]
@@ -1129,42 +1209,46 @@ class ApplianceTestSupport(ToilTest):
1129
1209
  packagePath_list = packagePath.split(".")
1130
1210
  packages, module = packagePath_list[:-1], packagePath_list[-1]
1131
1211
  for package in packages:
1132
- path += '/' + package
1133
- self.runOnAppliance('mkdir', '-p', path)
1134
- self.writeToAppliance(path + '/__init__.py', '')
1135
- 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)
1136
1216
 
1137
1217
  class LeaderThread(Appliance):
1138
1218
  def _entryPoint(self) -> str:
1139
- return 'mesos-master'
1219
+ return "mesos-master"
1140
1220
 
1141
1221
  def _getRole(self) -> str:
1142
- return 'leader'
1222
+ return "leader"
1143
1223
 
1144
- def _containerCommand(self) -> List[str]:
1145
- return ['--registry=in_memory',
1146
- '--ip=127.0.0.1',
1147
- '--port=5050',
1148
- '--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
+ ]
1149
1231
 
1150
1232
  class WorkerThread(Appliance):
1151
1233
  def __init__(
1152
- self, outer: "ApplianceTestSupport", mounts: Dict[str, str], numCores: int
1234
+ self, outer: "ApplianceTestSupport", mounts: dict[str, str], numCores: int
1153
1235
  ) -> None:
1154
1236
  self.numCores = numCores
1155
1237
  super().__init__(outer, mounts)
1156
1238
 
1157
1239
  def _entryPoint(self) -> str:
1158
- return 'mesos-agent'
1240
+ return "mesos-agent"
1159
1241
 
1160
1242
  def _getRole(self) -> str:
1161
- return 'worker'
1162
-
1163
- def _containerCommand(self) -> List[str]:
1164
- return ['--work_dir=/var/lib/mesos',
1165
- '--ip=127.0.0.1',
1166
- '--master=127.0.0.1:5050',
1167
- '--attributes=preemptible:False',
1168
- '--resources=cpus(*):%i' % self.numCores,
1169
- '--no-hostname_lookup',
1170
- '--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
+ ]