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/lib/docker.py CHANGED
@@ -17,15 +17,17 @@ import os
17
17
  import re
18
18
  import struct
19
19
  from shlex import quote
20
- from typing import List, Optional
20
+ from typing import Optional
21
21
 
22
22
  import requests
23
23
 
24
24
  import docker
25
- from docker.errors import (ContainerError,
26
- ImageNotFound,
27
- NotFound,
28
- create_api_error_from_http_exception)
25
+ from docker.errors import (
26
+ ContainerError,
27
+ ImageNotFound,
28
+ NotFound,
29
+ create_api_error_from_http_exception,
30
+ )
29
31
  from docker.utils.socket import consume_socket_output, demux_adaptor
30
32
  from toil.lib.accelerators import get_host_accelerator_numbers
31
33
 
@@ -37,42 +39,49 @@ RM = 2
37
39
 
38
40
 
39
41
  def dockerCheckOutput(*args, **kwargs):
40
- raise RuntimeError("dockerCheckOutput() using subprocess.check_output() has been removed, "
41
- "please switch to apiDockerCall().")
42
+ raise RuntimeError(
43
+ "dockerCheckOutput() using subprocess.check_output() has been removed, "
44
+ "please switch to apiDockerCall()."
45
+ )
42
46
 
43
47
 
44
48
  def dockerCall(*args, **kwargs):
45
- raise RuntimeError("dockerCall() using subprocess.check_output() has been removed, "
46
- "please switch to apiDockerCall().")
49
+ raise RuntimeError(
50
+ "dockerCall() using subprocess.check_output() has been removed, "
51
+ "please switch to apiDockerCall()."
52
+ )
47
53
 
48
54
 
49
55
  def subprocessDockerCall(*args, **kwargs):
50
- raise RuntimeError("subprocessDockerCall() has been removed, "
51
- "please switch to apiDockerCall().")
52
-
53
-
54
- def apiDockerCall(job,
55
- image,
56
- parameters=None,
57
- deferParam=None,
58
- volumes=None,
59
- working_dir=None,
60
- containerName=None,
61
- entrypoint=None,
62
- detach=False,
63
- log_config=None,
64
- auto_remove=None,
65
- remove=False,
66
- user=None,
67
- environment=None,
68
- stdout=None,
69
- stderr=False,
70
- stream=False,
71
- demux=False,
72
- streamfile=None,
73
- accelerators: Optional[List[int]] = None,
74
- timeout=365 * 24 * 60 * 60,
75
- **kwargs):
56
+ raise RuntimeError(
57
+ "subprocessDockerCall() has been removed, " "please switch to apiDockerCall()."
58
+ )
59
+
60
+
61
+ def apiDockerCall(
62
+ job,
63
+ image,
64
+ parameters=None,
65
+ deferParam=None,
66
+ volumes=None,
67
+ working_dir=None,
68
+ containerName=None,
69
+ entrypoint=None,
70
+ detach=False,
71
+ log_config=None,
72
+ auto_remove=None,
73
+ remove=False,
74
+ user=None,
75
+ environment=None,
76
+ stdout=None,
77
+ stderr=False,
78
+ stream=False,
79
+ demux=False,
80
+ streamfile=None,
81
+ accelerators: Optional[list[int]] = None,
82
+ timeout=365 * 24 * 60 * 60,
83
+ **kwargs,
84
+ ):
76
85
  """
77
86
  A toil wrapper for the python docker API.
78
87
 
@@ -184,7 +193,7 @@ def apiDockerCall(job,
184
193
  working_dir = os.getcwd()
185
194
 
186
195
  if volumes is None:
187
- volumes = {working_dir: {'bind': '/data', 'mode': 'rw'}}
196
+ volumes = {working_dir: {"bind": "/data", "mode": "rw"}}
188
197
 
189
198
  for volume in volumes:
190
199
  if not os.path.exists(volume):
@@ -197,11 +206,11 @@ def apiDockerCall(job,
197
206
  # and chain with pipes.
198
207
  if len(parameters) > 0 and type(parameters[0]) is list:
199
208
  if entrypoint is None:
200
- entrypoint = ['/bin/bash', '-c']
201
- chain_params = \
202
- [' '.join(quote(arg) for arg in command) \
203
- for command in parameters]
204
- command = ' | '.join(chain_params)
209
+ entrypoint = ["/bin/bash", "-c"]
210
+ chain_params = [
211
+ " ".join(quote(arg) for arg in command) for command in parameters
212
+ ]
213
+ command = " | ".join(chain_params)
205
214
  pipe_prefix = "set -eo pipefail && "
206
215
  command = [pipe_prefix + command]
207
216
  logger.debug("Calling docker with: " + repr(command))
@@ -213,7 +222,7 @@ def apiDockerCall(job,
213
222
  # practice:
214
223
  # http://docker-py.readthedocs.io/en/stable/containers.html
215
224
  elif len(parameters) > 0 and type(parameters) is list:
216
- command = ' '.join(quote(arg) for arg in parameters)
225
+ command = " ".join(quote(arg) for arg in parameters)
217
226
  logger.debug("Calling docker with: " + repr(command))
218
227
 
219
228
  # If the 'parameters' lists are empty, they are respecified as None, which
@@ -226,9 +235,9 @@ def apiDockerCall(job,
226
235
 
227
236
  # Ensure the user has passed a valid value for deferParam
228
237
  if deferParam not in (None, FORGO, STOP, RM):
229
- raise RuntimeError('Please provide a valid value for deferParam.')
238
+ raise RuntimeError("Please provide a valid value for deferParam.")
230
239
 
231
- client = docker.from_env(version='auto', timeout=timeout)
240
+ client = docker.from_env(version="auto", timeout=timeout)
232
241
 
233
242
  if deferParam is None:
234
243
  deferParam = RM
@@ -263,8 +272,7 @@ def apiDockerCall(job,
263
272
  # TODO: Here we assume that the host accelerators are all GPUs
264
273
  device_requests.append(
265
274
  docker.types.DeviceRequest(
266
- device_ids=[','.join(host_accelerators)],
267
- capabilities=[['gpu']]
275
+ device_ids=[",".join(host_accelerators)], capabilities=[["gpu"]]
268
276
  )
269
277
  )
270
278
 
@@ -275,24 +283,26 @@ def apiDockerCall(job,
275
283
  # 'hello world\n'
276
284
  if stdout is None:
277
285
  stdout = True
278
- out = client.containers.run(image=image,
279
- command=command,
280
- working_dir=working_dir,
281
- entrypoint=entrypoint,
282
- name=containerName,
283
- detach=False,
284
- volumes=volumes,
285
- auto_remove=auto_remove,
286
- stdout=stdout,
287
- stderr=stderr,
288
- # to get the generator if demux=True
289
- stream=stream or demux,
290
- remove=remove,
291
- log_config=log_config,
292
- user=user,
293
- environment=environment,
294
- device_requests=device_requests,
295
- **kwargs)
286
+ out = client.containers.run(
287
+ image=image,
288
+ command=command,
289
+ working_dir=working_dir,
290
+ entrypoint=entrypoint,
291
+ name=containerName,
292
+ detach=False,
293
+ volumes=volumes,
294
+ auto_remove=auto_remove,
295
+ stdout=stdout,
296
+ stderr=stderr,
297
+ # to get the generator if demux=True
298
+ stream=stream or demux,
299
+ remove=remove,
300
+ log_config=log_config,
301
+ user=user,
302
+ environment=environment,
303
+ device_requests=device_requests,
304
+ **kwargs,
305
+ )
296
306
 
297
307
  if demux is False:
298
308
  return out
@@ -300,7 +310,10 @@ def apiDockerCall(job,
300
310
  # If demux is True (i.e.: we want STDOUT and STDERR separated), we need to decode
301
311
  # the raw response from the docker API and preserve the stream type this time.
302
312
  response = out._response
303
- gen = (demux_adaptor(*frame) for frame in _multiplexed_response_stream_helper(response))
313
+ gen = (
314
+ demux_adaptor(*frame)
315
+ for frame in _multiplexed_response_stream_helper(response)
316
+ )
304
317
 
305
318
  if stream:
306
319
  return gen
@@ -309,9 +322,11 @@ def apiDockerCall(job,
309
322
 
310
323
  else:
311
324
  if (stdout or stderr) and log_config is None:
312
- logger.warning('stdout or stderr specified, but log_config is not set. '
313
- 'Defaulting to "journald".')
314
- log_config = dict(type='journald')
325
+ logger.warning(
326
+ "stdout or stderr specified, but log_config is not set. "
327
+ 'Defaulting to "journald".'
328
+ )
329
+ log_config = dict(type="journald")
315
330
 
316
331
  if stdout is None:
317
332
  stdout = False
@@ -319,30 +334,34 @@ def apiDockerCall(job,
319
334
  # When detach is True, this returns a container object:
320
335
  # >>> client.containers.run("bfirsh/reticulate-splines", detach=True)
321
336
  # <Container '45e6d2de7c54'>
322
- container = client.containers.run(image=image,
323
- command=command,
324
- working_dir=working_dir,
325
- entrypoint=entrypoint,
326
- name=containerName,
327
- detach=True,
328
- volumes=volumes,
329
- auto_remove=auto_remove,
330
- stdout=stdout,
331
- stderr=stderr,
332
- stream=stream,
333
- remove=remove,
334
- log_config=log_config,
335
- user=user,
336
- environment=environment,
337
- device_requests=device_requests,
338
- **kwargs)
337
+ container = client.containers.run(
338
+ image=image,
339
+ command=command,
340
+ working_dir=working_dir,
341
+ entrypoint=entrypoint,
342
+ name=containerName,
343
+ detach=True,
344
+ volumes=volumes,
345
+ auto_remove=auto_remove,
346
+ stdout=stdout,
347
+ stderr=stderr,
348
+ stream=stream,
349
+ remove=remove,
350
+ log_config=log_config,
351
+ user=user,
352
+ environment=environment,
353
+ device_requests=device_requests,
354
+ **kwargs,
355
+ )
339
356
  if stdout or stderr:
340
357
  if streamfile is None:
341
- streamfile = 'output.log'
342
- with open(streamfile, 'wb') as f:
358
+ streamfile = "output.log"
359
+ with open(streamfile, "wb") as f:
343
360
  # stream=True makes this loop blocking; we will loop until
344
361
  # the container stops and there is no more output.
345
- for line in container.logs(stdout=stdout, stderr=stderr, stream=True):
362
+ for line in container.logs(
363
+ stdout=stdout, stderr=stderr, stream=True
364
+ ):
346
365
  f.write(line.encode() if isinstance(line, str) else line)
347
366
 
348
367
  # If we didn't capture output, the caller will need to .wait() on
@@ -361,10 +380,12 @@ def apiDockerCall(job,
361
380
  raise create_api_error_from_http_exception(e)
362
381
 
363
382
 
364
- def dockerKill(container_name: str,
365
- gentleKill: bool = False,
366
- remove: bool = False,
367
- timeout: int = 365 * 24 * 60 * 60) -> None:
383
+ def dockerKill(
384
+ container_name: str,
385
+ gentleKill: bool = False,
386
+ remove: bool = False,
387
+ timeout: int = 365 * 24 * 60 * 60,
388
+ ) -> None:
368
389
  """
369
390
  Immediately kills a container. Equivalent to "docker kill":
370
391
  https://docs.docker.com/engine/reference/commandline/kill/
@@ -378,10 +399,10 @@ def dockerKill(container_name: str,
378
399
  to respect the timeout. Defaults to 1 year (i.e. wait
379
400
  essentially indefinitely).
380
401
  """
381
- client = docker.from_env(version='auto', timeout=timeout)
402
+ client = docker.from_env(version="auto", timeout=timeout)
382
403
  try:
383
404
  this_container = client.containers.get(container_name)
384
- while this_container.status == 'running':
405
+ while this_container.status == "running":
385
406
  if gentleKill is False:
386
407
  client.containers.get(container_name).kill()
387
408
  else:
@@ -390,9 +411,13 @@ def dockerKill(container_name: str,
390
411
  if remove:
391
412
  this_container.remove()
392
413
  except NotFound:
393
- logger.debug(f"Attempted to stop container ({container_name}), but container != exist.")
414
+ logger.debug(
415
+ f"Attempted to stop container ({container_name}), but container != exist."
416
+ )
394
417
  except requests.exceptions.HTTPError as e:
395
- logger.debug(f"Attempted to stop container ({container_name}), but server gave an error:")
418
+ logger.debug(
419
+ f"Attempted to stop container ({container_name}), but server gave an error:"
420
+ )
396
421
  raise create_api_error_from_http_exception(e)
397
422
 
398
423
 
@@ -419,10 +444,10 @@ def containerIsRunning(container_name: str, timeout: int = 365 * 24 * 60 * 60):
419
444
  :returns: True if status is 'running', False if status is anything else,
420
445
  and None if the container does not exist.
421
446
  """
422
- client = docker.from_env(version='auto', timeout=timeout)
447
+ client = docker.from_env(version="auto", timeout=timeout)
423
448
  try:
424
449
  this_container = client.containers.get(container_name)
425
- if this_container.status == 'running':
450
+ if this_container.status == "running":
426
451
  return True
427
452
  else:
428
453
  # this_container.status == 'exited', 'restarting', or 'paused'
@@ -430,8 +455,7 @@ def containerIsRunning(container_name: str, timeout: int = 365 * 24 * 60 * 60):
430
455
  except NotFound:
431
456
  return None
432
457
  except requests.exceptions.HTTPError as e:
433
- logger.debug("Server error attempting to call container: %s",
434
- container_name)
458
+ logger.debug("Server error attempting to call container: %s", container_name)
435
459
  raise create_api_error_from_http_exception(e)
436
460
 
437
461
 
@@ -440,8 +464,12 @@ def getContainerName(job):
440
464
  Create a random string including the job name, and return it. Name will
441
465
  match ``[a-zA-Z0-9][a-zA-Z0-9_.-]``.
442
466
  """
443
- parts = ['toil', str(job.description), base64.b64encode(os.urandom(9), b'-_').decode('utf-8')]
444
- name = re.sub('[^a-zA-Z0-9_.-]', '', '--'.join(parts))
467
+ parts = [
468
+ "toil",
469
+ str(job.description),
470
+ base64.b64encode(os.urandom(9), b"-_").decode("utf-8"),
471
+ ]
472
+ name = re.sub("[^a-zA-Z0-9_.-]", "", "--".join(parts))
445
473
  return name
446
474
 
447
475
 
@@ -459,7 +487,7 @@ def _multiplexed_response_stream_helper(response):
459
487
  break
460
488
  # header is 8 bytes with format: {STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4}
461
489
  # protocol: https://docs.docker.com/engine/api/v1.24/#attach-to-a-container
462
- stream_type, length = struct.unpack('>BxxxL', header)
490
+ stream_type, length = struct.unpack(">BxxxL", header)
463
491
  if not length:
464
492
  continue
465
493
  data = response.raw.read(length)