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
@@ -14,6 +14,7 @@
14
14
  import collections
15
15
  import datetime
16
16
  import errno
17
+ import fcntl
17
18
  import filecmp
18
19
  import inspect
19
20
  import logging
@@ -31,16 +32,20 @@ import pytest
31
32
  from toil.common import Toil
32
33
  from toil.exceptions import FailedJobsException
33
34
  from toil.fileStores import FileID
34
- from toil.fileStores.cachingFileStore import (CacheUnbalancedError,
35
- IllegalDeletionCacheError)
35
+ from toil.fileStores.cachingFileStore import (
36
+ CacheUnbalancedError,
37
+ IllegalDeletionCacheError,
38
+ )
36
39
  from toil.job import Job
37
40
  from toil.jobStores.abstractJobStore import NoSuchFileException
38
41
  from toil.realtimeLogger import RealtimeLogger
39
- from toil.test import (ToilTest,
40
- needs_aws_ec2,
41
- needs_google_project,
42
- needs_google_storage,
43
- slow)
42
+ from toil.test import (
43
+ ToilTest,
44
+ needs_aws_ec2,
45
+ needs_google_project,
46
+ needs_google_storage,
47
+ slow,
48
+ )
44
49
 
45
50
  # Some tests take too long on the AWS jobstore and are unquitable for CI. They can be
46
51
  # be run during manual tests by setting this to False.
@@ -54,41 +59,43 @@ class hidden:
54
59
  Hiding the abstract test classes from the Unittest loader so it can be inherited in different
55
60
  test suites for the different job stores.
56
61
  """
62
+
57
63
  class AbstractFileStoreTest(ToilTest, metaclass=ABCMeta):
58
64
  """
59
65
  An abstract base class for testing the various general functions described in
60
66
  :class:toil.fileStores.abstractFileStore.AbstractFileStore
61
67
  """
68
+
62
69
  # This is overwritten in the inheriting classs
63
70
  jobStoreType = None
64
71
 
65
72
  def _getTestJobStore(self):
66
- if self.jobStoreType == 'file':
73
+ if self.jobStoreType == "file":
67
74
  return self._getTestJobStorePath()
68
- elif self.jobStoreType == 'aws':
69
- return f'aws:{self.awsRegion()}:cache-tests-{str(uuid4())}'
70
- elif self.jobStoreType == 'google':
71
- projectID = os.getenv('TOIL_GOOGLE_PROJECTID')
72
- return f'google:{projectID}:cache-tests-{str(uuid4())}'
75
+ elif self.jobStoreType == "aws":
76
+ return f"aws:{self.awsRegion()}:cache-tests-{str(uuid4())}"
77
+ elif self.jobStoreType == "google":
78
+ projectID = os.getenv("TOIL_GOOGLE_PROJECTID")
79
+ return f"google:{projectID}:cache-tests-{str(uuid4())}"
73
80
  else:
74
- raise RuntimeError('Illegal job store type.')
81
+ raise RuntimeError("Illegal job store type.")
75
82
 
76
83
  def setUp(self):
77
84
  super().setUp()
78
85
  self.work_dir = self._createTempDir()
79
86
  self.options = Job.Runner.getDefaultOptions(self._getTestJobStore())
80
- self.options.logLevel = 'DEBUG'
87
+ self.options.logLevel = "DEBUG"
81
88
  self.options.realTimeLogging = True
82
89
  self.options.workDir = self.work_dir
83
- self.options.clean = 'always'
84
- self.options.logFile = os.path.join(self.work_dir, 'logFile')
90
+ self.options.clean = "always"
91
+ self.options.logFile = os.path.join(self.work_dir, "logFile")
85
92
 
86
93
  self.tmp_dir = self._createTempDir()
87
94
 
88
95
  def create_file(self, content, executable=False):
89
- file_path = f'{self.tmp_dir}/{uuid4()}'
96
+ file_path = f"{self.tmp_dir}/{uuid4()}"
90
97
 
91
- with open(file_path, 'w') as f:
98
+ with open(file_path, "w") as f:
92
99
  f.write(content)
93
100
 
94
101
  if executable:
@@ -131,6 +138,7 @@ class hidden:
131
138
  A logging handler that watches for a certain substring and
132
139
  trips a flag if it appears.
133
140
  """
141
+
134
142
  def __init__(self, match: str):
135
143
  super().__init__()
136
144
  self.match = match
@@ -144,8 +152,7 @@ class hidden:
144
152
 
145
153
  logging.getLogger().addHandler(handler)
146
154
 
147
- F = Job.wrapJobFn(self._accessAndFail,
148
- disk='100M')
155
+ F = Job.wrapJobFn(self._accessAndFail, disk="100M")
149
156
  try:
150
157
  Job.Runner.startToil(F, self.options)
151
158
  except FailedJobsException:
@@ -154,19 +161,20 @@ class hidden:
154
161
 
155
162
  logging.getLogger().removeHandler(handler)
156
163
 
157
- assert handler.seen, "Downloaded file name not found in logs of failing Toil run"
164
+ assert (
165
+ handler.seen
166
+ ), "Downloaded file name not found in logs of failing Toil run"
158
167
 
159
168
  @staticmethod
160
169
  def _accessAndFail(job):
161
170
  with job.fileStore.writeGlobalFileStream() as (writable, file_id):
162
- writable.write(b'Cats')
163
- localPath = os.path.join(job.fileStore.getLocalTempDir(), 'cats.txt')
171
+ writable.write(b"Cats")
172
+ localPath = os.path.join(job.fileStore.getLocalTempDir(), "cats.txt")
164
173
  job.fileStore.readGlobalFile(file_id, localPath)
165
174
  with job.fileStore.readGlobalFileStream(file_id) as readable:
166
175
  pass
167
176
  raise RuntimeError("I do not like this file")
168
177
 
169
-
170
178
  # Test filestore operations. This is a slightly less intense version of the cache specific
171
179
  # test `testReturnFileSizes`
172
180
  @slow
@@ -175,10 +183,13 @@ class hidden:
175
183
  Write a couple of files to the jobstore. Delete a couple of them. Read back written
176
184
  and locally deleted files.
177
185
  """
178
- workdir = self._createTempDir(purpose='nonLocalDir')
179
- F = Job.wrapJobFn(self._testFileStoreOperations,
180
- nonLocalDir=workdir,
181
- numIters=30, disk='2G')
186
+ workdir = self._createTempDir(purpose="nonLocalDir")
187
+ F = Job.wrapJobFn(
188
+ self._testFileStoreOperations,
189
+ nonLocalDir=workdir,
190
+ numIters=30,
191
+ disk="2G",
192
+ )
182
193
  Job.Runner.startToil(F, self.options)
183
194
 
184
195
  @staticmethod
@@ -192,14 +203,15 @@ class hidden:
192
203
  # Add one file for the sake of having something in the job store
193
204
  writeFileSize = random.randint(0, 30)
194
205
  cls = hidden.AbstractNonCachingFileStoreTest
195
- fsId, _ = cls._writeFileToJobStore(job, isLocalFile=True, nonLocalDir=nonLocalDir,
196
- fileMB=writeFileSize)
206
+ fsId, _ = cls._writeFileToJobStore(
207
+ job, isLocalFile=True, nonLocalDir=nonLocalDir, fileMB=writeFileSize
208
+ )
197
209
 
198
210
  # Fill in the size of the local file we just made
199
211
  writtenFiles[fsId] = writeFileSize
200
212
  # Remember it actually should be local
201
213
  localFileIDs.add(fsId)
202
- logger.info('Now have local file: %s', fsId)
214
+ logger.info("Now have local file: %s", fsId)
203
215
 
204
216
  i = 0
205
217
  while i <= numIters:
@@ -207,13 +219,21 @@ class hidden:
207
219
  if randVal < 0.33: # Write
208
220
  writeFileSize = random.randint(0, 30)
209
221
  isLocalFile = True if random.random() <= 0.5 else False
210
- fsID, _ = cls._writeFileToJobStore(job, isLocalFile=isLocalFile,
211
- nonLocalDir=nonLocalDir,
212
- fileMB=writeFileSize)
222
+ fsID, _ = cls._writeFileToJobStore(
223
+ job,
224
+ isLocalFile=isLocalFile,
225
+ nonLocalDir=nonLocalDir,
226
+ fileMB=writeFileSize,
227
+ )
213
228
  writtenFiles[fsID] = writeFileSize
214
229
  if isLocalFile:
215
230
  localFileIDs.add(fsID)
216
- logger.info('Wrote %s file of size %d MB: %s', 'local' if isLocalFile else 'non-local', writeFileSize, fsID)
231
+ logger.info(
232
+ "Wrote %s file of size %d MB: %s",
233
+ "local" if isLocalFile else "non-local",
234
+ writeFileSize,
235
+ fsID,
236
+ )
217
237
  else:
218
238
  if len(writtenFiles) == 0:
219
239
  continue
@@ -223,10 +243,19 @@ class hidden:
223
243
  if randVal < 0.66: # Read
224
244
  mutable = True if random.random() <= 0.5 else False
225
245
  cache = True if random.random() <= 0.5 else False
226
- job.fileStore.readGlobalFile(fsID, '/'.join([work_dir, str(uuid4())]),
227
- cache=cache, mutable=mutable)
246
+ job.fileStore.readGlobalFile(
247
+ fsID,
248
+ "/".join([work_dir, str(uuid4())]),
249
+ cache=cache,
250
+ mutable=mutable,
251
+ )
228
252
  localFileIDs.add(fsID)
229
- logger.info('Read %s %s local copy of: %s', 'mutable' if mutable else 'immutable', 'cached' if cache else 'uncached', fsID)
253
+ logger.info(
254
+ "Read %s %s local copy of: %s",
255
+ "mutable" if mutable else "immutable",
256
+ "cached" if cache else "uncached",
257
+ fsID,
258
+ )
230
259
  else: # Delete
231
260
  if rdelRandVal <= 0.5: # Local Delete
232
261
  if fsID not in localFileIDs:
@@ -239,18 +268,23 @@ class hidden:
239
268
  # ENOENT. If it doesn't something is
240
269
  # broken.
241
270
  raise
242
- logger.info('Correctly fail to local-delete non-local file: %s', fsID)
271
+ logger.info(
272
+ "Correctly fail to local-delete non-local file: %s",
273
+ fsID,
274
+ )
243
275
  else:
244
- assert False, f"Was able to delete non-local file {fsID}"
276
+ assert (
277
+ False
278
+ ), f"Was able to delete non-local file {fsID}"
245
279
  else:
246
- logger.info('Delete local file: %s', fsID)
280
+ logger.info("Delete local file: %s", fsID)
247
281
  job.fileStore.deleteLocalFile(fsID)
248
282
  else: # Global Delete
249
283
  job.fileStore.deleteGlobalFile(fsID)
250
284
  writtenFiles.pop(fsID)
251
285
  if fsID in localFileIDs:
252
286
  localFileIDs.remove(fsID)
253
- logger.info('No longer have file: %s', fsID)
287
+ logger.info("No longer have file: %s", fsID)
254
288
  i += 1
255
289
 
256
290
  def testWriteReadGlobalFilePermissions(self):
@@ -261,18 +295,23 @@ class hidden:
261
295
  """
262
296
  for executable in True, False:
263
297
  for caching in True, False:
264
- with self.subTest(f'Testing readwrite file permissions\n'
265
- f'[executable: {executable}]\n'
266
- f'[caching: {caching}]\n'):
298
+ with self.subTest(
299
+ f"Testing readwrite file permissions\n"
300
+ f"[executable: {executable}]\n"
301
+ f"[caching: {caching}]\n"
302
+ ):
267
303
  self.options.caching = caching
268
- read_write_job = Job.wrapJobFn(self._testWriteReadGlobalFilePermissions, executable=executable)
304
+ read_write_job = Job.wrapJobFn(
305
+ self._testWriteReadGlobalFilePermissions,
306
+ executable=executable,
307
+ )
269
308
  Job.Runner.startToil(read_write_job, self.options)
270
309
 
271
310
  @staticmethod
272
311
  def _testWriteReadGlobalFilePermissions(job, executable):
273
312
  srcFile = job.fileStore.getLocalTempFile()
274
- with open(srcFile, 'w') as f:
275
- f.write('Hello')
313
+ with open(srcFile, "w") as f:
314
+ f.write("Hello")
276
315
 
277
316
  if executable:
278
317
  os.chmod(srcFile, os.stat(srcFile).st_mode | stat.S_IXUSR)
@@ -284,10 +323,14 @@ class hidden:
284
323
  for mutable in True, False:
285
324
  for symlink in True, False:
286
325
  dstFile = job.fileStore.getLocalTempFileName()
287
- job.fileStore.readGlobalFile(fileID, userPath=dstFile, mutable=mutable, symlink=symlink)
326
+ job.fileStore.readGlobalFile(
327
+ fileID, userPath=dstFile, mutable=mutable, symlink=symlink
328
+ )
288
329
  # Current file owner execute permissions
289
330
  currentPermissions = os.stat(dstFile).st_mode & stat.S_IXUSR
290
- assert initialPermissions == currentPermissions, f'{initialPermissions} != {currentPermissions}'
331
+ assert (
332
+ initialPermissions == currentPermissions
333
+ ), f"{initialPermissions} != {currentPermissions}"
291
334
 
292
335
  def testWriteExportFileCompatibility(self):
293
336
  """
@@ -295,20 +338,24 @@ class hidden:
295
338
  when they are exported from the leader.
296
339
  """
297
340
  for executable in True, False:
298
- export_file_job = Job.wrapJobFn(self._testWriteExportFileCompatibility, executable=executable)
341
+ export_file_job = Job.wrapJobFn(
342
+ self._testWriteExportFileCompatibility, executable=executable
343
+ )
299
344
  with Toil(self.options) as toil:
300
345
  initialPermissions, fileID = toil.start(export_file_job)
301
346
  dstFile = os.path.join(self._createTempDir(), str(uuid4()))
302
- toil.exportFile(fileID, 'file://' + dstFile)
347
+ toil.exportFile(fileID, "file://" + dstFile)
303
348
  currentPermissions = os.stat(dstFile).st_mode & stat.S_IXUSR
304
349
 
305
- assert initialPermissions == currentPermissions, f'{initialPermissions} != {currentPermissions}'
350
+ assert (
351
+ initialPermissions == currentPermissions
352
+ ), f"{initialPermissions} != {currentPermissions}"
306
353
 
307
354
  @staticmethod
308
355
  def _testWriteExportFileCompatibility(job, executable):
309
356
  srcFile = job.fileStore.getLocalTempFile()
310
- with open(srcFile, 'w') as f:
311
- f.write('Hello')
357
+ with open(srcFile, "w") as f:
358
+ f.write("Hello")
312
359
  if executable:
313
360
  os.chmod(srcFile, os.stat(srcFile).st_mode | stat.S_IXUSR)
314
361
  initialPermissions = os.stat(srcFile).st_mode & stat.S_IXUSR
@@ -322,22 +369,30 @@ class hidden:
322
369
  """
323
370
  with Toil(self.options) as toil:
324
371
  for executable in True, False:
325
- file_path = self.create_file(content='Hello', executable=executable)
372
+ file_path = self.create_file(content="Hello", executable=executable)
326
373
  initial_permissions = os.stat(file_path).st_mode & stat.S_IXUSR
327
- file_id = toil.importFile(f'file://{file_path}')
374
+ file_id = toil.importFile(f"file://{file_path}")
328
375
  for mutable in True, False:
329
376
  for symlink in True, False:
330
- with self.subTest(f'Now testing readGlobalFileWith: mutable={mutable} symlink={symlink}'):
331
- A = Job.wrapJobFn(self._testImportReadFileCompatibility,
332
- fileID=file_id,
333
- initialPermissions=initial_permissions,
334
- mutable=mutable,
335
- symlink=symlink)
377
+ with self.subTest(
378
+ f"Now testing readGlobalFileWith: mutable={mutable} symlink={symlink}"
379
+ ):
380
+ A = Job.wrapJobFn(
381
+ self._testImportReadFileCompatibility,
382
+ fileID=file_id,
383
+ initialPermissions=initial_permissions,
384
+ mutable=mutable,
385
+ symlink=symlink,
386
+ )
336
387
  toil.start(A)
337
388
 
338
389
  @staticmethod
339
- def _testImportReadFileCompatibility(job, fileID, initialPermissions, mutable, symlink):
340
- dstFile = job.fileStore.readGlobalFile(fileID, mutable=mutable, symlink=symlink)
390
+ def _testImportReadFileCompatibility(
391
+ job, fileID, initialPermissions, mutable, symlink
392
+ ):
393
+ dstFile = job.fileStore.readGlobalFile(
394
+ fileID, mutable=mutable, symlink=symlink
395
+ )
341
396
  currentPermissions = os.stat(dstFile).st_mode & stat.S_IXUSR
342
397
 
343
398
  assert initialPermissions == currentPermissions
@@ -352,11 +407,16 @@ class hidden:
352
407
 
353
408
  @staticmethod
354
409
  def _testReadWriteFileStreamTextMode(job):
355
- with job.fileStore.writeGlobalFileStream(encoding='utf-8') as (stream, fileID):
356
- stream.write('foo')
410
+ with job.fileStore.writeGlobalFileStream(encoding="utf-8") as (
411
+ stream,
412
+ fileID,
413
+ ):
414
+ stream.write("foo")
357
415
  job.fileStore.readGlobalFileStream(fileID)
358
- with job.fileStore.readGlobalFileStream(fileID, encoding='utf-8') as stream2:
359
- assert 'foo' == stream2.read()
416
+ with job.fileStore.readGlobalFileStream(
417
+ fileID, encoding="utf-8"
418
+ ) as stream2:
419
+ assert "foo" == stream2.read()
360
420
 
361
421
  @staticmethod
362
422
  def _writeFileToJobStore(job, isLocalFile, nonLocalDir=None, fileMB=1):
@@ -373,7 +433,7 @@ class hidden:
373
433
  else:
374
434
  assert nonLocalDir is not None
375
435
  work_dir = nonLocalDir
376
- with open(os.path.join(work_dir, str(uuid4())), 'wb') as testFile:
436
+ with open(os.path.join(work_dir, str(uuid4())), "wb") as testFile:
377
437
  testFile.write(os.urandom(fileMB * 1024 * 1024))
378
438
 
379
439
  return job.fileStore.writeGlobalFile(testFile.name), testFile
@@ -408,7 +468,7 @@ class hidden:
408
468
  the chain. This tests whether the cache is created properly even when the job crashes
409
469
  randomly.
410
470
  """
411
- if testingIsAutomatic and self.jobStoreType != 'file':
471
+ if testingIsAutomatic and self.jobStoreType != "file":
412
472
  self.skipTest("To save time")
413
473
  self.options.retryCount = 10
414
474
  self.options.badWorker = 0.25
@@ -435,7 +495,7 @@ class hidden:
435
495
 
436
496
  # Explicitly set clean to always so even the failed cases get cleaned (This will
437
497
  # overwrite the value set in setUp if it is ever changed in the future)
438
- self.options.clean = 'always'
498
+ self.options.clean = "always"
439
499
 
440
500
  self._testCacheEviction(file1MB=20, file2MB=30, diskRequestMB=10)
441
501
 
@@ -451,7 +511,7 @@ class hidden:
451
511
 
452
512
  # Explicitly set clean to always so even the failed cases get cleaned (This will
453
513
  # overwrite the value set in setUp if it is ever changed in the future)
454
- self.options.clean = 'always'
514
+ self.options.clean = "always"
455
515
 
456
516
  self._testCacheEviction(file1MB=20, file2MB=30, diskRequestMB=30)
457
517
 
@@ -467,7 +527,7 @@ class hidden:
467
527
 
468
528
  # Explicitly set clean to always so even the failed cases get cleaned (This will
469
529
  # overwrite the value set in setUp if it is ever changed in the future)
470
- self.options.clean = 'always'
530
+ self.options.clean = "always"
471
531
 
472
532
  self._testCacheEviction(file1MB=20, file2MB=30, diskRequestMB=60)
473
533
 
@@ -475,7 +535,7 @@ class hidden:
475
535
  # If the job store and cache are on the same file system, file
476
536
  # sizes are accounted for by the job store and are not reflected in
477
537
  # the cache hence this test is redundant (caching will be free).
478
- if not self.options.jobStore.startswith(('aws', 'google')):
538
+ if not self.options.jobStore.startswith(("aws", "google")):
479
539
  workDirDev = os.stat(self.options.workDir).st_dev
480
540
  if self.options.jobStore.startswith("file:"):
481
541
  # Before #4538, options.jobStore would have the raw path while the Config object would prepend the
@@ -483,11 +543,15 @@ class hidden:
483
543
  # The options namespace and the Config object now have the exact same behavior
484
544
  # which means parse_jobstore will be called with argparse rather than with the config object
485
545
  # so remove the prepended file: scheme
486
- jobStoreDev = os.stat(os.path.dirname(self.options.jobStore[5:])).st_dev
546
+ jobStoreDev = os.stat(
547
+ os.path.dirname(self.options.jobStore[5:])
548
+ ).st_dev
487
549
  else:
488
550
  jobStoreDev = os.stat(os.path.dirname(self.options.jobStore)).st_dev
489
551
  if workDirDev == jobStoreDev:
490
- self.skipTest('Job store and working directory are on the same filesystem.')
552
+ self.skipTest(
553
+ "Job store and working directory are on the same filesystem."
554
+ )
491
555
 
492
556
  def _testCacheEviction(self, file1MB, file2MB, diskRequestMB):
493
557
  """
@@ -500,23 +564,35 @@ class hidden:
500
564
  self.options.retryCount = 0
501
565
  if diskRequestMB > 50:
502
566
  # This can be non int as it will never reach _probeJobReqs
503
- expectedResult = 'Fail'
567
+ expectedResult = "Fail"
504
568
  else:
505
569
  expectedResult = 50 - file1MB if diskRequestMB <= file1MB else 0
506
570
  try:
507
- A = Job.wrapJobFn(self._writeFileToJobStoreWithAsserts, isLocalFile=True,
508
- fileMB=file1MB)
571
+ A = Job.wrapJobFn(
572
+ self._writeFileToJobStoreWithAsserts,
573
+ isLocalFile=True,
574
+ fileMB=file1MB,
575
+ )
509
576
  # Sleep for 1 second after writing the first file so that their ctimes are
510
577
  # guaranteed to be distinct for the purpose of this test.
511
578
  B = Job.wrapJobFn(self._sleepy, timeToSleep=1)
512
- C = Job.wrapJobFn(self._writeFileToJobStoreWithAsserts, isLocalFile=True,
513
- fileMB=file2MB)
514
- D = Job.wrapJobFn(self._adjustCacheLimit, newTotalMB=50, disk='0Mi')
515
- E = Job.wrapJobFn(self._uselessFunc, disk=''.join([str(diskRequestMB), 'Mi']))
579
+ C = Job.wrapJobFn(
580
+ self._writeFileToJobStoreWithAsserts,
581
+ isLocalFile=True,
582
+ fileMB=file2MB,
583
+ )
584
+ D = Job.wrapJobFn(self._adjustCacheLimit, newTotalMB=50, disk="0Mi")
585
+ E = Job.wrapJobFn(
586
+ self._uselessFunc, disk="".join([str(diskRequestMB), "Mi"])
587
+ )
516
588
  # Set it to > 2GB such that the cleanup jobs don't die in the non-fail cases
517
- F = Job.wrapJobFn(self._adjustCacheLimit, newTotalMB=5000, disk='10Mi')
518
- G = Job.wrapJobFn(self._probeJobReqs, sigmaJob=100, cached=expectedResult,
519
- disk='100Mi')
589
+ F = Job.wrapJobFn(self._adjustCacheLimit, newTotalMB=5000, disk="10Mi")
590
+ G = Job.wrapJobFn(
591
+ self._probeJobReqs,
592
+ sigmaJob=100,
593
+ cached=expectedResult,
594
+ disk="100Mi",
595
+ )
520
596
  A.addChild(B)
521
597
  B.addChild(C)
522
598
  C.addChild(D)
@@ -528,12 +604,16 @@ class hidden:
528
604
  with open(self.options.logFile) as f:
529
605
  logContents = f.read()
530
606
  if CacheUnbalancedError.message in logContents:
531
- self.assertEqual(expectedResult, 'Fail')
607
+ self.assertEqual(expectedResult, "Fail")
532
608
  else:
533
- self.fail('Toil did not raise the expected CacheUnbalancedError but failed for some other reason')
609
+ self.fail(
610
+ "Toil did not raise the expected CacheUnbalancedError but failed for some other reason"
611
+ )
534
612
 
535
613
  @staticmethod
536
- def _writeFileToJobStoreWithAsserts(job, isLocalFile, nonLocalDir=None, fileMB=1, expectAsyncUpload=True):
614
+ def _writeFileToJobStoreWithAsserts(
615
+ job, isLocalFile, nonLocalDir=None, fileMB=1, expectAsyncUpload=True
616
+ ):
537
617
  """
538
618
  This function creates a file and writes it to the jobstore.
539
619
 
@@ -547,7 +627,9 @@ class hidden:
547
627
  the job store later(T) or immediately(F)
548
628
  """
549
629
  cls = hidden.AbstractNonCachingFileStoreTest
550
- fsID, testFile = cls._writeFileToJobStore(job, isLocalFile, nonLocalDir, fileMB)
630
+ fsID, testFile = cls._writeFileToJobStore(
631
+ job, isLocalFile, nonLocalDir, fileMB
632
+ )
551
633
  actual = os.stat(testFile.name).st_nlink
552
634
 
553
635
  # If the caching is free, the job store must have hard links to
@@ -565,13 +647,19 @@ class hidden:
565
647
  # We also expect a link in the job store
566
648
  expected += 1
567
649
 
568
- assert actual == expected, 'Should have %d links. Got %d.' % (expected, actual)
650
+ assert actual == expected, "Should have %d links. Got %d." % (
651
+ expected,
652
+ actual,
653
+ )
569
654
 
570
- logger.info('Uploaded %s with %d links', fsID, actual)
655
+ logger.info("Uploaded %s with %d links", fsID, actual)
571
656
 
572
657
  if not isLocalFile:
573
658
  # Make sure it isn't cached if we don't want it to be
574
- assert not job.fileStore.fileIsCached(fsID), "File uploaded from non-local-temp directory %s should not be cached" % nonLocalDir
659
+ assert not job.fileStore.fileIsCached(fsID), (
660
+ "File uploaded from non-local-temp directory %s should not be cached"
661
+ % nonLocalDir
662
+ )
575
663
 
576
664
  return fsID
577
665
 
@@ -608,30 +696,36 @@ class hidden:
608
696
  :param int sigmaJob: Expected sum of job requirements in MB.
609
697
  """
610
698
 
611
- RealtimeLogger.info('Probing job requirements')
699
+ RealtimeLogger.info("Probing job requirements")
612
700
 
613
701
  valueDict = locals()
614
- assert (total or cached or sigmaJob)
702
+ assert total or cached or sigmaJob
615
703
 
616
704
  # Work out which function to call for which value
617
- toCall = {'total': job.fileStore.getCacheLimit,
618
- 'cached': job.fileStore.getCacheUsed,
619
- 'sigmaJob': job.fileStore.getCacheExtraJobSpace}
705
+ toCall = {
706
+ "total": job.fileStore.getCacheLimit,
707
+ "cached": job.fileStore.getCacheUsed,
708
+ "sigmaJob": job.fileStore.getCacheExtraJobSpace,
709
+ }
620
710
 
621
- for value in ('total', 'cached', 'sigmaJob'):
711
+ for value in ("total", "cached", "sigmaJob"):
622
712
  # If the value wasn't provided, it is None and should be ignored
623
713
  if valueDict[value] is None:
624
714
  continue
625
715
 
626
- RealtimeLogger.info('Probing cache state: %s', value)
716
+ RealtimeLogger.info("Probing cache state: %s", value)
627
717
 
628
718
  expectedBytes = valueDict[value] * 1024 * 1024
629
719
  cacheInfoBytes = toCall[value]()
630
720
 
631
- RealtimeLogger.info('Got %d for %s; expected %d', cacheInfoBytes, value, expectedBytes)
721
+ RealtimeLogger.info(
722
+ "Got %d for %s; expected %d", cacheInfoBytes, value, expectedBytes
723
+ )
632
724
 
633
- assert cacheInfoBytes == expectedBytes, 'Testing %s: Expected ' % value + \
634
- f'{expectedBytes} but got {cacheInfoBytes}.'
725
+ assert cacheInfoBytes == expectedBytes, (
726
+ "Testing %s: Expected " % value
727
+ + f"{expectedBytes} but got {cacheInfoBytes}."
728
+ )
635
729
 
636
730
  @slow
637
731
  def testAsyncWriteWithCaching(self):
@@ -641,7 +735,7 @@ class hidden:
641
735
  file into cache then rewrites it to the job store triggering an async write since the
642
736
  two unique jobstore IDs point to the same local file. Also, the second write is not
643
737
  cached since the first was written to cache, and there "isn't enough space" to cache the
644
- second. Imediately assert that the second write isn't cached, and is being
738
+ second. Immediately assert that the second write isn't cached, and is being
645
739
  asynchronously written to the job store.
646
740
 
647
741
  Attempting to get the file from the jobstore should not fail.
@@ -649,12 +743,14 @@ class hidden:
649
743
  print("Testing")
650
744
  logger.debug("Testing testing 123")
651
745
  self.options.retryCount = 0
652
- self.options.logLevel = 'DEBUG'
653
- A = Job.wrapJobFn(self._adjustCacheLimit, newTotalMB=1024, disk='1G')
654
- B = Job.wrapJobFn(self._doubleWriteFileToJobStore, fileMB=850, disk='900M')
655
- C = Job.wrapJobFn(self._readFromJobStoreWithoutAssertions, fsID=B.rv(), disk='1G')
746
+ self.options.logLevel = "DEBUG"
747
+ A = Job.wrapJobFn(self._adjustCacheLimit, newTotalMB=1024, disk="1G")
748
+ B = Job.wrapJobFn(self._doubleWriteFileToJobStore, fileMB=850, disk="900M")
749
+ C = Job.wrapJobFn(
750
+ self._readFromJobStoreWithoutAssertions, fsID=B.rv(), disk="1G"
751
+ )
656
752
  # Set it to > 2GB such that the cleanup jobs don't die.
657
- D = Job.wrapJobFn(self._adjustCacheLimit, newTotalMB=5000, disk='1G')
753
+ D = Job.wrapJobFn(self._adjustCacheLimit, newTotalMB=5000, disk="1G")
658
754
  A.addChild(B)
659
755
  B.addChild(C)
660
756
  C.addChild(D)
@@ -670,20 +766,22 @@ class hidden:
670
766
  :param fileMB: File Size
671
767
  :return: Job store file ID for second written file
672
768
  """
673
- job.fileStore.log_to_leader('Double writing a file into job store')
769
+ job.fileStore.log_to_leader("Double writing a file into job store")
674
770
  work_dir = job.fileStore.getLocalTempDir()
675
- with open(os.path.join(work_dir, str(uuid4())), 'wb') as testFile:
771
+ with open(os.path.join(work_dir, str(uuid4())), "wb") as testFile:
676
772
  testFile.write(os.urandom(fileMB * 1024 * 1024))
677
773
 
678
- job.fileStore.log_to_leader('Writing copy 1 and discarding ID')
774
+ job.fileStore.log_to_leader("Writing copy 1 and discarding ID")
679
775
  job.fileStore.writeGlobalFile(testFile.name)
680
- job.fileStore.log_to_leader('Writing copy 2 and saving ID')
776
+ job.fileStore.log_to_leader("Writing copy 2 and saving ID")
681
777
  fsID = job.fileStore.writeGlobalFile(testFile.name)
682
- job.fileStore.log_to_leader(f'Copy 2 ID: {fsID}')
778
+ job.fileStore.log_to_leader(f"Copy 2 ID: {fsID}")
683
779
 
684
- hidden.AbstractCachingFileStoreTest._readFromJobStoreWithoutAssertions(job, fsID)
780
+ hidden.AbstractCachingFileStoreTest._readFromJobStoreWithoutAssertions(
781
+ job, fsID
782
+ )
685
783
 
686
- job.fileStore.log_to_leader('Writing copy 3 and returning ID')
784
+ job.fileStore.log_to_leader("Writing copy 3 and returning ID")
687
785
  return job.fileStore.writeGlobalFile(testFile.name)
688
786
 
689
787
  @staticmethod
@@ -695,7 +793,7 @@ class hidden:
695
793
  :param fsID: Job store file ID for the read file
696
794
  :return: None
697
795
  """
698
- job.fileStore.log_to_leader('Reading the written file')
796
+ job.fileStore.log_to_leader("Reading the written file")
699
797
  job.fileStore.readGlobalFile(fsID)
700
798
 
701
799
  # writeGlobalFile tests
@@ -705,9 +803,12 @@ class hidden:
705
803
  Write a file not in localTempDir to the job store. Such a file should not be cached.
706
804
  Ensure the file is not cached.
707
805
  """
708
- workdir = self._createTempDir(purpose='nonLocalDir')
709
- A = Job.wrapJobFn(self._writeFileToJobStoreWithAsserts, isLocalFile=False,
710
- nonLocalDir=workdir)
806
+ workdir = self._createTempDir(purpose="nonLocalDir")
807
+ A = Job.wrapJobFn(
808
+ self._writeFileToJobStoreWithAsserts,
809
+ isLocalFile=False,
810
+ nonLocalDir=workdir,
811
+ )
711
812
  Job.Runner.startToil(A, self.options)
712
813
 
713
814
  def testWriteLocalFileToJobStore(self):
@@ -740,11 +841,18 @@ class hidden:
740
841
 
741
842
  :param cacheReadFile: Does the read file need to be cached(T) or not(F)
742
843
  """
743
- workdir = self._createTempDir(purpose='nonLocalDir')
744
- A = Job.wrapJobFn(self._writeFileToJobStoreWithAsserts, isLocalFile=False,
745
- nonLocalDir=workdir)
746
- B = Job.wrapJobFn(self._readFromJobStore, isCachedFile=False,
747
- cacheReadFile=cacheReadFile, fsID=A.rv())
844
+ workdir = self._createTempDir(purpose="nonLocalDir")
845
+ A = Job.wrapJobFn(
846
+ self._writeFileToJobStoreWithAsserts,
847
+ isLocalFile=False,
848
+ nonLocalDir=workdir,
849
+ )
850
+ B = Job.wrapJobFn(
851
+ self._readFromJobStore,
852
+ isCachedFile=False,
853
+ cacheReadFile=cacheReadFile,
854
+ fsID=A.rv(),
855
+ )
748
856
  A.addChild(B)
749
857
  Job.Runner.startToil(A, self.options)
750
858
 
@@ -767,25 +875,38 @@ class hidden:
767
875
  work_dir = job.fileStore.getLocalTempDir()
768
876
  wantHardLink = False
769
877
  if isCachedFile:
770
- outfile = job.fileStore.readGlobalFile(fsID, '/'.join([work_dir, 'temp']),
771
- mutable=False)
878
+ outfile = job.fileStore.readGlobalFile(
879
+ fsID, "/".join([work_dir, "temp"]), mutable=False
880
+ )
772
881
  wantHardLink = True
773
882
  else:
774
883
  if cacheReadFile:
775
- outfile = job.fileStore.readGlobalFile(fsID, '/'.join([work_dir, 'temp']),
776
- cache=True, mutable=False)
884
+ outfile = job.fileStore.readGlobalFile(
885
+ fsID, "/".join([work_dir, "temp"]), cache=True, mutable=False
886
+ )
777
887
  wantHardLink = True
778
888
  else:
779
- assert not job.fileStore.fileIsCached(fsID), "File mistakenly cached before read"
780
- outfile = job.fileStore.readGlobalFile(fsID, '/'.join([work_dir, 'temp']),
781
- cache=False, mutable=False)
782
- assert not job.fileStore.fileIsCached(fsID), "File mistakenly cached after read"
889
+ assert not job.fileStore.fileIsCached(
890
+ fsID
891
+ ), "File mistakenly cached before read"
892
+ outfile = job.fileStore.readGlobalFile(
893
+ fsID, "/".join([work_dir, "temp"]), cache=False, mutable=False
894
+ )
895
+ assert not job.fileStore.fileIsCached(
896
+ fsID
897
+ ), "File mistakenly cached after read"
783
898
  wantHardLink = False
784
899
  if isTest:
785
900
  actual = os.stat(outfile).st_nlink
786
901
  if wantHardLink:
787
- assert actual > 1, 'Should have multiple links for file that was %s and %s. Got %i.' % ('cached' if isCachedFile else 'not cached',
788
- 'saved' if cacheReadFile else 'not saved', actual)
902
+ assert actual > 1, (
903
+ "Should have multiple links for file that was %s and %s. Got %i."
904
+ % (
905
+ "cached" if isCachedFile else "not cached",
906
+ "saved" if cacheReadFile else "not saved",
907
+ actual,
908
+ )
909
+ )
789
910
  # We need to accept harf links even if we don't want them,
790
911
  # because we may get them straight from the FileJobStore since
791
912
  # we asked for immutable reads.
@@ -799,8 +920,12 @@ class hidden:
799
920
  of links on the file are appropriate.
800
921
  """
801
922
  A = Job.wrapJobFn(self._writeFileToJobStoreWithAsserts, isLocalFile=True)
802
- B = Job.wrapJobFn(self._readFromJobStore, isCachedFile=True, cacheReadFile=None,
803
- fsID=A.rv())
923
+ B = Job.wrapJobFn(
924
+ self._readFromJobStore,
925
+ isCachedFile=True,
926
+ cacheReadFile=None,
927
+ fsID=A.rv(),
928
+ )
804
929
  A.addChild(B)
805
930
  Job.Runner.startToil(A, self.options)
806
931
 
@@ -830,23 +955,33 @@ class hidden:
830
955
 
831
956
  :param bool cacheHit: Is the test for the CacheHit case(T) or cacheMiss case(F)
832
957
  """
833
- dirPurpose = 'tempWriteDir' if cacheHit else 'nonLocalDir'
958
+ dirPurpose = "tempWriteDir" if cacheHit else "nonLocalDir"
834
959
  workdir = self._createTempDir(purpose=dirPurpose)
835
- with open(os.path.join(workdir, 'test'), 'w') as x:
960
+ file_name = os.path.join(workdir, "test")
961
+ with open(file_name, "w") as x:
836
962
  x.write(str(0))
837
- A = Job.wrapJobFn(self._writeFileToJobStoreWithAsserts, isLocalFile=cacheHit,
838
- nonLocalDir=workdir,
839
- fileMB=256)
840
- B = Job.wrapJobFn(self._probeJobReqs, sigmaJob=100, disk='100Mi')
963
+ A = Job.wrapJobFn(
964
+ self._writeFileToJobStoreWithAsserts,
965
+ isLocalFile=cacheHit,
966
+ nonLocalDir=workdir,
967
+ fileMB=256,
968
+ )
969
+ B = Job.wrapJobFn(self._probeJobReqs, sigmaJob=100, disk="100Mi")
841
970
  jobs = {}
842
971
  for i in range(0, 10):
843
- jobs[i] = Job.wrapJobFn(self._multipleFileReader, diskMB=1024, fsID=A.rv(),
844
- maxWriteFile=os.path.abspath(x.name), disk='1Gi',
845
- memory='10Mi', cores=1)
972
+ jobs[i] = Job.wrapJobFn(
973
+ self._multipleFileReader,
974
+ diskMB=1024,
975
+ fsID=A.rv(),
976
+ maxWriteFile=os.path.abspath(file_name),
977
+ disk="1Gi",
978
+ memory="10Mi",
979
+ cores=1,
980
+ )
846
981
  A.addChild(jobs[i])
847
982
  jobs[i].addChild(B)
848
983
  Job.Runner.startToil(A, self.options)
849
- with open(x.name) as y:
984
+ with open(file_name) as y:
850
985
  # At least one job at a time should have been observed.
851
986
  # We can't actually guarantee that any of our jobs will
852
987
  # see each other currently running.
@@ -867,29 +1002,35 @@ class hidden:
867
1002
  file will be written
868
1003
  """
869
1004
  work_dir = job.fileStore.getLocalTempDir()
870
- outfile = job.fileStore.readGlobalFile(fsID, '/'.join([work_dir, 'temp']), cache=True,
871
- mutable=False)
1005
+ outfile = job.fileStore.readGlobalFile(
1006
+ fsID, "/".join([work_dir, "temp"]), cache=True, mutable=False
1007
+ )
872
1008
  diskBytes = diskMB * 1024 * 1024
873
1009
  fileStats = os.stat(outfile)
874
1010
  fileSize = fileStats.st_size
875
1011
 
876
1012
  currentReaders = job.fileStore.getFileReaderCount(fsID)
1013
+ # This should always count us
1014
+ assert currentReaders > 0
877
1015
 
878
1016
  extraJobSpace = job.fileStore.getCacheExtraJobSpace()
879
1017
 
880
1018
  usedCache = job.fileStore.getCacheUsed()
881
1019
 
882
- logger.info('Extra job space: %s', str(extraJobSpace))
883
- logger.info('Current file readers: %s', str(currentReaders))
884
- logger.info('File size: %s', str(fileSize))
885
- logger.info('Job disk bytes: %s', str(diskBytes))
886
- logger.info('Used cache: %s', str(usedCache))
1020
+ logger.info("Extra job space: %s", str(extraJobSpace))
1021
+ logger.info("Current file readers: %s", str(currentReaders))
1022
+ logger.info("File size: %s", str(fileSize))
1023
+ logger.info("Job disk bytes: %s", str(diskBytes))
1024
+ logger.info("Used cache: %s", str(usedCache))
887
1025
 
888
- with open(maxWriteFile, 'r+') as x:
1026
+ with open(maxWriteFile, "r+") as x:
1027
+ # Advisory lock the file we are saving max readers to
1028
+ fcntl.lockf(x, fcntl.LOCK_EX)
889
1029
  prev_max = int(x.read())
890
1030
  x.seek(0)
891
1031
  x.truncate()
892
1032
  x.write(str(max(prev_max, currentReaders)))
1033
+ fcntl.lockf(x, fcntl.LOCK_UN)
893
1034
  if job.fileStore.cachingIsFree():
894
1035
  # No space should be used when caching is free
895
1036
  assert usedCache == 0.0
@@ -898,23 +1039,26 @@ class hidden:
898
1039
  assert usedCache == fileSize
899
1040
 
900
1041
  # Make sure that there's no over-usage of job requirements
901
- assert ((extraJobSpace + currentReaders * fileSize) %
902
- diskBytes) == 0.0
1042
+ assert ((extraJobSpace + currentReaders * fileSize) % diskBytes) == 0.0
903
1043
  # Sleep so there's no race conditions where a job ends before another can get a hold of
904
1044
  # the file
905
1045
  time.sleep(3)
906
1046
 
907
1047
  @staticmethod
908
1048
  def _writeExportGlobalFile(job):
909
- fileName = os.path.join(job.fileStore.getLocalTempDir(), 'testfile')
910
- with open(fileName, 'wb') as f:
911
- f.write(os.urandom(1024 * 30000)) # 30 Mb
912
- outputFile = os.path.join(job.fileStore.getLocalTempDir(), 'exportedFile')
913
- job.fileStore.export_file(job.fileStore.writeGlobalFile(fileName), 'File://' + outputFile)
1049
+ fileName = os.path.join(job.fileStore.getLocalTempDir(), "testfile")
1050
+ with open(fileName, "wb") as f:
1051
+ f.write(os.urandom(1024 * 30000)) # 30 Mb
1052
+ outputFile = os.path.join(job.fileStore.getLocalTempDir(), "exportedFile")
1053
+ job.fileStore.export_file(
1054
+ job.fileStore.writeGlobalFile(fileName), "File://" + outputFile
1055
+ )
914
1056
  if not filecmp.cmp(fileName, outputFile):
915
- logger.warning('Source file: %s', str(os.stat(fileName)))
916
- logger.warning('Destination file: %s', str(os.stat(outputFile)))
917
- raise RuntimeError(f"File {fileName} did not properly get copied to {outputFile}")
1057
+ logger.warning("Source file: %s", str(os.stat(fileName)))
1058
+ logger.warning("Destination file: %s", str(os.stat(outputFile)))
1059
+ raise RuntimeError(
1060
+ f"File {fileName} did not properly get copied to {outputFile}"
1061
+ )
918
1062
 
919
1063
  @slow
920
1064
  def testFileStoreExportFile(self):
@@ -931,12 +1075,14 @@ class hidden:
931
1075
  Read back written and locally deleted files. Ensure that after
932
1076
  every step that the cache is in a valid state.
933
1077
  """
934
- workdir = self._createTempDir(purpose='nonLocalDir')
935
- F = Job.wrapJobFn(self._returnFileTestFn,
936
- jobDisk=2 * 1024 * 1024 * 1024,
937
- initialCachedSize=0,
938
- nonLocalDir=workdir,
939
- disk='2Gi')
1078
+ workdir = self._createTempDir(purpose="nonLocalDir")
1079
+ F = Job.wrapJobFn(
1080
+ self._returnFileTestFn,
1081
+ jobDisk=2 * 1024 * 1024 * 1024,
1082
+ initialCachedSize=0,
1083
+ nonLocalDir=workdir,
1084
+ disk="2Gi",
1085
+ )
940
1086
  Job.Runner.startToil(F, self.options)
941
1087
 
942
1088
  @slow
@@ -949,16 +1095,21 @@ class hidden:
949
1095
  self.options.retryCount = 20
950
1096
  self.options.badWorker = 0.5
951
1097
  self.options.badWorkerFailInterval = 0.1
952
- workdir = self._createTempDir(purpose='nonLocalDir')
953
- F = Job.wrapJobFn(self._returnFileTestFn,
954
- jobDisk=2 * 1024 * 1024 * 1024,
955
- initialCachedSize=0,
956
- nonLocalDir=workdir,
957
- numIters=30, disk='2Gi')
1098
+ workdir = self._createTempDir(purpose="nonLocalDir")
1099
+ F = Job.wrapJobFn(
1100
+ self._returnFileTestFn,
1101
+ jobDisk=2 * 1024 * 1024 * 1024,
1102
+ initialCachedSize=0,
1103
+ nonLocalDir=workdir,
1104
+ numIters=30,
1105
+ disk="2Gi",
1106
+ )
958
1107
  Job.Runner.startToil(F, self.options)
959
1108
 
960
1109
  @staticmethod
961
- def _returnFileTestFn(job, jobDisk, initialCachedSize, nonLocalDir, numIters=100):
1110
+ def _returnFileTestFn(
1111
+ job, jobDisk, initialCachedSize, nonLocalDir, numIters=100
1112
+ ):
962
1113
  """
963
1114
  Aux function for jobCacheTest.testReturnFileSizes Conduct numIters operations and ensure
964
1115
  the cache has the right amount of data in it at all times.
@@ -970,7 +1121,7 @@ class hidden:
970
1121
  :param float jobDisk: The value of disk passed to this job.
971
1122
  """
972
1123
  cached = initialCachedSize
973
- RealtimeLogger.info('Expecting %d bytes cached initially', cached)
1124
+ RealtimeLogger.info("Expecting %d bytes cached initially", cached)
974
1125
  work_dir = job.fileStore.getLocalTempDir()
975
1126
  writtenFiles = {} # fsID: (size, isLocal)
976
1127
  # fsid: local/mutable/immutable for all operations that should make local files as tracked by the FileStore
@@ -981,45 +1132,71 @@ class hidden:
981
1132
  # We keep jobDisk in sync with the amount of free space the job
982
1133
  # still has that the file store doesn't know it has used.
983
1134
  cls = hidden.AbstractCachingFileStoreTest
984
- fsId = cls._writeFileToJobStoreWithAsserts(job, isLocalFile=True, fileMB=writeFileSize)
1135
+ fsId = cls._writeFileToJobStoreWithAsserts(
1136
+ job, isLocalFile=True, fileMB=writeFileSize
1137
+ )
985
1138
  writtenFiles[fsId] = writeFileSize
986
1139
  if job.fileStore.fileIsCached(list(writtenFiles.keys())[0]):
987
1140
  cached += writeFileSize * 1024 * 1024
988
- RealtimeLogger.info('Expecting %d bytes cached because file of %d MB is cached', cached, writeFileSize)
1141
+ RealtimeLogger.info(
1142
+ "Expecting %d bytes cached because file of %d MB is cached",
1143
+ cached,
1144
+ writeFileSize,
1145
+ )
989
1146
  else:
990
- RealtimeLogger.info('Expecting %d bytes cached because file of %d MB is not cached', cached, writeFileSize)
991
- localFileIDs[list(writtenFiles.keys())[0]].append('local')
992
- RealtimeLogger.info('Checking for %d bytes cached', cached)
1147
+ RealtimeLogger.info(
1148
+ "Expecting %d bytes cached because file of %d MB is not cached",
1149
+ cached,
1150
+ writeFileSize,
1151
+ )
1152
+ localFileIDs[list(writtenFiles.keys())[0]].append("local")
1153
+ RealtimeLogger.info("Checking for %d bytes cached", cached)
993
1154
  cls._requirementsConcur(job, jobDisk, cached)
994
1155
  i = 0
995
1156
  while i <= numIters:
996
1157
  randVal = random.random()
997
1158
  if randVal < 0.33: # Write
998
- RealtimeLogger.info('Writing a file')
1159
+ RealtimeLogger.info("Writing a file")
999
1160
  writeFileSize = random.randint(0, 30)
1000
1161
  if random.random() <= 0.5: # Write a local file
1001
- RealtimeLogger.info('Writing a local file of %d MB', writeFileSize)
1002
- fsID = cls._writeFileToJobStoreWithAsserts(job, isLocalFile=True,
1003
- fileMB=writeFileSize)
1004
- RealtimeLogger.info('Wrote local file: %s', fsID)
1162
+ RealtimeLogger.info(
1163
+ "Writing a local file of %d MB", writeFileSize
1164
+ )
1165
+ fsID = cls._writeFileToJobStoreWithAsserts(
1166
+ job, isLocalFile=True, fileMB=writeFileSize
1167
+ )
1168
+ RealtimeLogger.info("Wrote local file: %s", fsID)
1005
1169
  writtenFiles[fsID] = writeFileSize
1006
- localFileIDs[fsID].append('local')
1170
+ localFileIDs[fsID].append("local")
1007
1171
  jobDisk -= writeFileSize * 1024 * 1024
1008
1172
  if job.fileStore.fileIsCached(fsID):
1009
1173
  cached += writeFileSize * 1024 * 1024
1010
- RealtimeLogger.info('Expecting %d bytes cached because file of %d MB is cached', cached, writeFileSize)
1174
+ RealtimeLogger.info(
1175
+ "Expecting %d bytes cached because file of %d MB is cached",
1176
+ cached,
1177
+ writeFileSize,
1178
+ )
1011
1179
  else:
1012
- RealtimeLogger.info('Expecting %d bytes cached because file of %d MB is not cached', cached, writeFileSize)
1180
+ RealtimeLogger.info(
1181
+ "Expecting %d bytes cached because file of %d MB is not cached",
1182
+ cached,
1183
+ writeFileSize,
1184
+ )
1013
1185
  else: # Write a non-local file
1014
- RealtimeLogger.info('Writing a non-local file of %d MB', writeFileSize)
1015
- fsID = cls._writeFileToJobStoreWithAsserts(job, isLocalFile=False,
1016
- nonLocalDir=nonLocalDir,
1017
- fileMB=writeFileSize)
1018
- RealtimeLogger.info('Wrote non-local file: %s', fsID)
1186
+ RealtimeLogger.info(
1187
+ "Writing a non-local file of %d MB", writeFileSize
1188
+ )
1189
+ fsID = cls._writeFileToJobStoreWithAsserts(
1190
+ job,
1191
+ isLocalFile=False,
1192
+ nonLocalDir=nonLocalDir,
1193
+ fileMB=writeFileSize,
1194
+ )
1195
+ RealtimeLogger.info("Wrote non-local file: %s", fsID)
1019
1196
  writtenFiles[fsID] = writeFileSize
1020
1197
  # Don't record in localFileIDs because we're not local
1021
1198
  # No change to the job since there was no caching
1022
- RealtimeLogger.info('Checking for %d bytes cached', cached)
1199
+ RealtimeLogger.info("Checking for %d bytes cached", cached)
1023
1200
  cls._requirementsConcur(job, jobDisk, cached)
1024
1201
  else:
1025
1202
  if len(writtenFiles) == 0:
@@ -1029,61 +1206,101 @@ class hidden:
1029
1206
  rdelRandVal = random.random()
1030
1207
  fileWasCached = job.fileStore.fileIsCached(fsID)
1031
1208
  if randVal < 0.66: # Read
1032
- RealtimeLogger.info('Reading a file with size %d and previous cache status %s: %s', rdelFileSize, str(fileWasCached), fsID)
1209
+ RealtimeLogger.info(
1210
+ "Reading a file with size %d and previous cache status %s: %s",
1211
+ rdelFileSize,
1212
+ str(fileWasCached),
1213
+ fsID,
1214
+ )
1033
1215
  if rdelRandVal <= 0.5: # Read as mutable, uncached
1034
- RealtimeLogger.info('Reading as mutable and uncached; should still have %d bytes cached', cached)
1035
- job.fileStore.readGlobalFile(fsID, '/'.join([work_dir, str(uuid4())]),
1036
- mutable=True, cache=False)
1037
- localFileIDs[fsID].append('mutable')
1216
+ RealtimeLogger.info(
1217
+ "Reading as mutable and uncached; should still have %d bytes cached",
1218
+ cached,
1219
+ )
1220
+ job.fileStore.readGlobalFile(
1221
+ fsID,
1222
+ "/".join([work_dir, str(uuid4())]),
1223
+ mutable=True,
1224
+ cache=False,
1225
+ )
1226
+ localFileIDs[fsID].append("mutable")
1038
1227
  # No change because the file wasn't cached
1039
1228
  else: # Read as immutable
1040
- RealtimeLogger.info('Reading as immutable and cacheable')
1041
- job.fileStore.readGlobalFile(fsID, '/'.join([work_dir, str(uuid4())]),
1042
- mutable=False, cache=True)
1043
- localFileIDs[fsID].append('immutable')
1229
+ RealtimeLogger.info("Reading as immutable and cacheable")
1230
+ job.fileStore.readGlobalFile(
1231
+ fsID,
1232
+ "/".join([work_dir, str(uuid4())]),
1233
+ mutable=False,
1234
+ cache=True,
1235
+ )
1236
+ localFileIDs[fsID].append("immutable")
1044
1237
  jobDisk -= rdelFileSize * 1024 * 1024
1045
1238
  if not fileWasCached:
1046
1239
  if job.fileStore.fileIsCached(fsID):
1047
- RealtimeLogger.info('File was not cached before and is now. Should have %d bytes cached', cached)
1240
+ RealtimeLogger.info(
1241
+ "File was not cached before and is now. Should have %d bytes cached",
1242
+ cached,
1243
+ )
1048
1244
  cached += rdelFileSize * 1024 * 1024
1049
1245
  else:
1050
- RealtimeLogger.info('File was not cached before and still is not now. '
1051
- 'Should still have %d bytes cached', cached)
1246
+ RealtimeLogger.info(
1247
+ "File was not cached before and still is not now. "
1248
+ "Should still have %d bytes cached",
1249
+ cached,
1250
+ )
1052
1251
  else:
1053
- RealtimeLogger.info('File was cached before. Should still have %d bytes cached', cached)
1252
+ RealtimeLogger.info(
1253
+ "File was cached before. Should still have %d bytes cached",
1254
+ cached,
1255
+ )
1054
1256
  cls._requirementsConcur(job, jobDisk, cached)
1055
1257
  else: # Delete
1056
1258
  if rdelRandVal <= 0.5: # Local Delete
1057
1259
  if fsID not in list(localFileIDs.keys()):
1058
1260
  continue
1059
- RealtimeLogger.info('Deleting a file locally with history %s: %s', localFileIDs[fsID], fsID)
1261
+ RealtimeLogger.info(
1262
+ "Deleting a file locally with history %s: %s",
1263
+ localFileIDs[fsID],
1264
+ fsID,
1265
+ )
1060
1266
  job.fileStore.deleteLocalFile(fsID)
1061
1267
  else: # Global Delete
1062
- RealtimeLogger.info('Deleting a file globally: %s', fsID)
1268
+ RealtimeLogger.info("Deleting a file globally: %s", fsID)
1063
1269
  job.fileStore.deleteGlobalFile(fsID)
1064
1270
  try:
1065
1271
  job.fileStore.readGlobalFile(fsID)
1066
1272
  except FileNotFoundError as err:
1067
1273
  pass
1068
1274
  except:
1069
- raise RuntimeError('Got wrong error type for read of deleted file')
1275
+ raise RuntimeError(
1276
+ "Got wrong error type for read of deleted file"
1277
+ )
1070
1278
  else:
1071
- raise RuntimeError('Able to read deleted file')
1279
+ raise RuntimeError("Able to read deleted file")
1072
1280
  writtenFiles.pop(fsID)
1073
1281
  if fsID in list(localFileIDs.keys()):
1074
1282
  for lFID in localFileIDs[fsID]:
1075
- if lFID != 'mutable':
1283
+ if lFID != "mutable":
1076
1284
  jobDisk += rdelFileSize * 1024 * 1024
1077
1285
  localFileIDs.pop(fsID)
1078
1286
  if fileWasCached:
1079
1287
  if not job.fileStore.fileIsCached(fsID):
1080
1288
  cached -= rdelFileSize * 1024 * 1024
1081
- RealtimeLogger.info('File was cached before and is not now. Should have %d bytes cached', cached)
1289
+ RealtimeLogger.info(
1290
+ "File was cached before and is not now. Should have %d bytes cached",
1291
+ cached,
1292
+ )
1082
1293
  else:
1083
- RealtimeLogger.info('File was cached before and still is cached now. '
1084
- 'Should still have %d bytes cached', cached)
1294
+ RealtimeLogger.info(
1295
+ "File was cached before and still is cached now. "
1296
+ "Should still have %d bytes cached",
1297
+ cached,
1298
+ )
1085
1299
  else:
1086
- RealtimeLogger.info('File was not cached before deletion. Should still have %d bytes cached', cached)
1300
+ RealtimeLogger.info(
1301
+ "File was not cached before deletion. Should still have %d bytes cached",
1302
+ cached,
1303
+ )
1087
1304
  cls._requirementsConcur(job, jobDisk, cached)
1088
1305
  i += 1
1089
1306
  return jobDisk, cached
@@ -1098,15 +1315,32 @@ class hidden:
1098
1315
  used = job.fileStore.getCacheUsed()
1099
1316
 
1100
1317
  if not job.fileStore.cachingIsFree():
1101
- RealtimeLogger.info('Caching is not free; %d bytes are used and %d bytes are expected', used, cached)
1102
- assert used == cached, 'Cache should have %d bytes used, but actually has %d bytes used' % (cached, used)
1318
+ RealtimeLogger.info(
1319
+ "Caching is not free; %d bytes are used and %d bytes are expected",
1320
+ used,
1321
+ cached,
1322
+ )
1323
+ assert used == cached, (
1324
+ "Cache should have %d bytes used, but actually has %d bytes used"
1325
+ % (cached, used)
1326
+ )
1103
1327
  else:
1104
- RealtimeLogger.info('Caching is free; %d bytes are used and %d bytes would be expected if caching were not free', used, cached)
1105
- assert used == 0, 'Cache should have nothing in it, but actually has %d bytes used' % used
1328
+ RealtimeLogger.info(
1329
+ "Caching is free; %d bytes are used and %d bytes would be expected if caching were not free",
1330
+ used,
1331
+ cached,
1332
+ )
1333
+ assert used == 0, (
1334
+ "Cache should have nothing in it, but actually has %d bytes used"
1335
+ % used
1336
+ )
1106
1337
 
1107
1338
  jobUnused = job.fileStore.getCacheUnusedJobRequirement()
1108
1339
 
1109
- assert jobUnused == jobDisk, 'Job should have %d bytes of disk for non-FileStore use but the FileStore reports %d' % (jobDisk, jobUnused)
1340
+ assert jobUnused == jobDisk, (
1341
+ "Job should have %d bytes of disk for non-FileStore use but the FileStore reports %d"
1342
+ % (jobDisk, jobUnused)
1343
+ )
1110
1344
 
1111
1345
  # Testing the resumability of a failed worker
1112
1346
  @slow
@@ -1115,13 +1349,16 @@ class hidden:
1115
1349
  Conduct a couple of job store operations. Then die. Ensure that the restarted job is
1116
1350
  tracking values in the cache state file appropriately.
1117
1351
  """
1118
- workdir = self._createTempDir(purpose='nonLocalDir')
1352
+ workdir = self._createTempDir(purpose="nonLocalDir")
1119
1353
  self.options.retryCount = 1
1120
1354
  jobDiskBytes = 2 * 1024 * 1024 * 1024
1121
- F = Job.wrapJobFn(self._controlledFailTestFn, jobDisk=jobDiskBytes,
1122
- testDir=workdir,
1123
- disk=jobDiskBytes)
1124
- G = Job.wrapJobFn(self._probeJobReqs, sigmaJob=100, disk='100Mi')
1355
+ F = Job.wrapJobFn(
1356
+ self._controlledFailTestFn,
1357
+ jobDisk=jobDiskBytes,
1358
+ testDir=workdir,
1359
+ disk=jobDiskBytes,
1360
+ )
1361
+ G = Job.wrapJobFn(self._probeJobReqs, sigmaJob=100, disk="100Mi")
1125
1362
  F.addChild(G)
1126
1363
  Job.Runner.startToil(F, self.options)
1127
1364
 
@@ -1137,22 +1374,35 @@ class hidden:
1137
1374
  """
1138
1375
 
1139
1376
  # Make sure we actually have the disk size we are supposed to
1140
- job.fileStore.log_to_leader('Job is running with %d bytes of disk, %d requested' % (job.disk, jobDisk))
1141
- assert job.disk == jobDisk, 'Job was scheduled with %d bytes but requested %d' % (job.disk, jobDisk)
1377
+ job.fileStore.log_to_leader(
1378
+ "Job is running with %d bytes of disk, %d requested"
1379
+ % (job.disk, jobDisk)
1380
+ )
1381
+ assert (
1382
+ job.disk == jobDisk
1383
+ ), "Job was scheduled with %d bytes but requested %d" % (job.disk, jobDisk)
1142
1384
 
1143
1385
  cls = hidden.AbstractCachingFileStoreTest
1144
- if os.path.exists(os.path.join(testDir, 'testfile.test')):
1145
- with open(os.path.join(testDir, 'testfile.test'), 'rb') as fH:
1146
- cached = unpack('d', fH.read())[0]
1147
- RealtimeLogger.info('Loaded expected cache size of %d from testfile.test', cached)
1386
+ if os.path.exists(os.path.join(testDir, "testfile.test")):
1387
+ with open(os.path.join(testDir, "testfile.test"), "rb") as fH:
1388
+ cached = unpack("d", fH.read())[0]
1389
+ RealtimeLogger.info(
1390
+ "Loaded expected cache size of %d from testfile.test", cached
1391
+ )
1148
1392
  cls._requirementsConcur(job, jobDisk, cached)
1149
1393
  cls._returnFileTestFn(job, jobDisk, cached, testDir, 20)
1150
1394
  else:
1151
- RealtimeLogger.info('Expecting cache size of 0 because testfile.test is absent')
1152
- modifiedJobReqs, cached = cls._returnFileTestFn(job, jobDisk, 0, testDir, 20)
1153
- with open(os.path.join(testDir, 'testfile.test'), 'wb') as fH:
1154
- fH.write(pack('d', cached))
1155
- RealtimeLogger.info('Wrote cache size of %d to testfile.test', cached)
1395
+ RealtimeLogger.info(
1396
+ "Expecting cache size of 0 because testfile.test is absent"
1397
+ )
1398
+ modifiedJobReqs, cached = cls._returnFileTestFn(
1399
+ job, jobDisk, 0, testDir, 20
1400
+ )
1401
+ with open(os.path.join(testDir, "testfile.test"), "wb") as fH:
1402
+ fH.write(pack("d", cached))
1403
+ RealtimeLogger.info(
1404
+ "Wrote cache size of %d to testfile.test", cached
1405
+ )
1156
1406
  os.kill(os.getpid(), signal.SIGKILL)
1157
1407
 
1158
1408
  @slow
@@ -1171,9 +1421,15 @@ class hidden:
1171
1421
 
1172
1422
  def _deleteLocallyReadFilesFn(self, readAsMutable):
1173
1423
  self.options.retryCount = 0
1174
- A = Job.wrapJobFn(self._writeFileToJobStoreWithAsserts, isLocalFile=True, memory='10M')
1175
- B = Job.wrapJobFn(self._removeReadFileFn, A.rv(), readAsMutable=readAsMutable,
1176
- memory='20M')
1424
+ A = Job.wrapJobFn(
1425
+ self._writeFileToJobStoreWithAsserts, isLocalFile=True, memory="10M"
1426
+ )
1427
+ B = Job.wrapJobFn(
1428
+ self._removeReadFileFn,
1429
+ A.rv(),
1430
+ readAsMutable=readAsMutable,
1431
+ memory="20M",
1432
+ )
1177
1433
  A.addChild(B)
1178
1434
  Job.Runner.startToil(A, self.options)
1179
1435
 
@@ -1191,9 +1447,10 @@ class hidden:
1191
1447
  # Are we processing the read file or the written file?
1192
1448
  processsingReadFile = True
1193
1449
  # Read in the file
1194
- outfile = job.fileStore.readGlobalFile(fileToDelete, os.path.join(work_dir, 'temp'),
1195
- mutable=readAsMutable)
1196
- tempfile = os.path.join(work_dir, 'tmp.tmp')
1450
+ outfile = job.fileStore.readGlobalFile(
1451
+ fileToDelete, os.path.join(work_dir, "temp"), mutable=readAsMutable
1452
+ )
1453
+ tempfile = os.path.join(work_dir, "tmp.tmp")
1197
1454
  # The first time we run this loop, processsingReadFile is True and fileToDelete is the
1198
1455
  # file read from the job store. The second time, processsingReadFile is False and
1199
1456
  # fileToDelete is one that was just written in to the job store. Ensure the correct
@@ -1203,7 +1460,9 @@ class hidden:
1203
1460
  try:
1204
1461
  job.fileStore.deleteLocalFile(fileToDelete)
1205
1462
  except IllegalDeletionCacheError:
1206
- job.fileStore.log_to_leader('Detected a deleted file %s.' % fileToDelete)
1463
+ job.fileStore.log_to_leader(
1464
+ "Detected a deleted file %s." % fileToDelete
1465
+ )
1207
1466
  os.rename(tempfile, outfile)
1208
1467
  else:
1209
1468
  # If we are processing the write test, or if we are testing the immutably read
@@ -1212,7 +1471,7 @@ class hidden:
1212
1471
  if processsingReadFile:
1213
1472
  processsingReadFile = False
1214
1473
  # Write a file
1215
- with open(os.path.join(work_dir, str(uuid4())), 'wb') as testFile:
1474
+ with open(os.path.join(work_dir, str(uuid4())), "wb") as testFile:
1216
1475
  testFile.write(os.urandom(1 * 1024 * 1024))
1217
1476
  fileToDelete = job.fileStore.writeGlobalFile(testFile.name)
1218
1477
  outfile = testFile.name
@@ -1224,7 +1483,7 @@ class hidden:
1224
1483
  Test the deletion capabilities of deleteLocalFile
1225
1484
  """
1226
1485
  self.options.retryCount = 0
1227
- workdir = self._createTempDir(purpose='nonLocalDir')
1486
+ workdir = self._createTempDir(purpose="nonLocalDir")
1228
1487
  A = Job.wrapJobFn(self._deleteLocalFileFn, nonLocalDir=workdir)
1229
1488
  Job.Runner.startToil(A, self.options)
1230
1489
 
@@ -1236,11 +1495,11 @@ class hidden:
1236
1495
  """
1237
1496
  work_dir = job.fileStore.getLocalTempDir()
1238
1497
  # Write local file
1239
- with open(os.path.join(work_dir, str(uuid4())), 'wb') as localFile:
1498
+ with open(os.path.join(work_dir, str(uuid4())), "wb") as localFile:
1240
1499
  localFile.write(os.urandom(1 * 1024 * 1024))
1241
1500
  localFsID = job.fileStore.writeGlobalFile(localFile.name)
1242
1501
  # write Non-Local File
1243
- with open(os.path.join(nonLocalDir, str(uuid4())), 'wb') as nonLocalFile:
1502
+ with open(os.path.join(nonLocalDir, str(uuid4())), "wb") as nonLocalFile:
1244
1503
  nonLocalFile.write(os.urandom(1 * 1024 * 1024))
1245
1504
  nonLocalFsID = job.fileStore.writeGlobalFile(nonLocalFile.name)
1246
1505
  # Delete fsid of local file. The file should be deleted
@@ -1270,7 +1529,7 @@ class hidden:
1270
1529
  assert not os.path.exists(readBackFile2)
1271
1530
  # Try to get a non-FileID that doesn't exist.
1272
1531
  try:
1273
- job.fileStore.readGlobalFile('bogus')
1532
+ job.fileStore.readGlobalFile("bogus")
1274
1533
  except NoSuchFileException:
1275
1534
  # TODO: We would like to require TypeError, but for Cactus
1276
1535
  # support we have to accept non-FileIDs.
@@ -1279,7 +1538,7 @@ class hidden:
1279
1538
  raise RuntimeError("Managed to get a file from a non-FileID")
1280
1539
  # Try to get a FileID for something that doesn't exist
1281
1540
  try:
1282
- job.fileStore.readGlobalFile(FileID('bogus', 4096))
1541
+ job.fileStore.readGlobalFile(FileID("bogus", 4096))
1283
1542
  except NoSuchFileException:
1284
1543
  pass
1285
1544
  else:
@@ -1307,7 +1566,7 @@ class hidden:
1307
1566
  Create and return a FileID for a non-cached file written via a stream.
1308
1567
  """
1309
1568
 
1310
- messageBytes = b'This is a test file\n'
1569
+ messageBytes = b"This is a test file\n"
1311
1570
 
1312
1571
  with job.fileStore.jobStore.write_file_stream() as (out, idString):
1313
1572
  # Write directly to the job store so the caching file store doesn't even see it.
@@ -1320,7 +1579,9 @@ class hidden:
1320
1579
  return fileID
1321
1580
 
1322
1581
  @staticmethod
1323
- def _readFileWithDelay(job, fileID, cores=0.1, memory=50 * 1024 * 1024, disk=50 * 1024 * 1024):
1582
+ def _readFileWithDelay(
1583
+ job, fileID, cores=0.1, memory=50 * 1024 * 1024, disk=50 * 1024 * 1024
1584
+ ):
1324
1585
  """
1325
1586
  Read a file from the CachingFileStore with a delay imposed on the download.
1326
1587
  Should create contention.
@@ -1333,43 +1594,47 @@ class hidden:
1333
1594
  job.fileStore.forceDownloadDelay = 120
1334
1595
 
1335
1596
  readStart = datetime.datetime.now()
1336
- logger.debug('Begin read at %s', str(readStart))
1597
+ logger.debug("Begin read at %s", str(readStart))
1337
1598
 
1338
1599
  localPath = job.fileStore.readGlobalFile(fileID, cache=True, mutable=True)
1339
1600
 
1340
1601
  readEnd = datetime.datetime.now()
1341
- logger.debug('End read at %s: took %f seconds', str(readEnd), (readEnd - readStart).total_seconds())
1342
-
1343
- with open(localPath, 'rb') as fh:
1344
- text = fh.read().decode('utf-8').strip()
1345
- logger.debug('Got file contents: %s', text)
1602
+ logger.debug(
1603
+ "End read at %s: took %f seconds",
1604
+ str(readEnd),
1605
+ (readEnd - readStart).total_seconds(),
1606
+ )
1346
1607
 
1608
+ with open(localPath, "rb") as fh:
1609
+ text = fh.read().decode("utf-8").strip()
1610
+ logger.debug("Got file contents: %s", text)
1347
1611
 
1348
1612
 
1349
1613
  class NonCachingFileStoreTestWithFileJobStore(hidden.AbstractNonCachingFileStoreTest):
1350
- jobStoreType = 'file'
1614
+ jobStoreType = "file"
1615
+
1351
1616
 
1352
1617
  @pytest.mark.timeout(1000)
1353
1618
  class CachingFileStoreTestWithFileJobStore(hidden.AbstractCachingFileStoreTest):
1354
- jobStoreType = 'file'
1619
+ jobStoreType = "file"
1355
1620
 
1356
1621
 
1357
1622
  @needs_aws_ec2
1358
1623
  class NonCachingFileStoreTestWithAwsJobStore(hidden.AbstractNonCachingFileStoreTest):
1359
- jobStoreType = 'aws'
1624
+ jobStoreType = "aws"
1360
1625
 
1361
1626
 
1362
1627
  @slow
1363
1628
  @needs_aws_ec2
1364
1629
  @pytest.mark.timeout(1000)
1365
1630
  class CachingFileStoreTestWithAwsJobStore(hidden.AbstractCachingFileStoreTest):
1366
- jobStoreType = 'aws'
1631
+ jobStoreType = "aws"
1367
1632
 
1368
1633
 
1369
1634
  @needs_google_project
1370
1635
  @needs_google_storage
1371
1636
  class NonCachingFileStoreTestWithGoogleJobStore(hidden.AbstractNonCachingFileStoreTest):
1372
- jobStoreType = 'google'
1637
+ jobStoreType = "google"
1373
1638
 
1374
1639
 
1375
1640
  @slow
@@ -1377,7 +1642,7 @@ class NonCachingFileStoreTestWithGoogleJobStore(hidden.AbstractNonCachingFileSto
1377
1642
  @needs_google_storage
1378
1643
  @pytest.mark.timeout(1000)
1379
1644
  class CachingFileStoreTestWithGoogleJobStore(hidden.AbstractCachingFileStoreTest):
1380
- jobStoreType = 'google'
1645
+ jobStoreType = "google"
1381
1646
 
1382
1647
 
1383
1648
  def _exportStaticMethodAsGlobalFunctions(cls):
@@ -1386,10 +1651,12 @@ def _exportStaticMethodAsGlobalFunctions(cls):
1386
1651
  the convention that the first argument of a job function is named 'job'.
1387
1652
  """
1388
1653
  for name, kind, clazz, value in inspect.classify_class_attrs(cls):
1389
- if kind == 'static method' and name != '__new__': # __new__ became static in 3.7
1654
+ if (
1655
+ kind == "static method" and name != "__new__"
1656
+ ): # __new__ became static in 3.7
1390
1657
  method = value.__func__
1391
1658
  args = inspect.getfullargspec(method).args
1392
- if args and args[0] == 'job':
1659
+ if args and args[0] == "job":
1393
1660
  globals()[name] = method
1394
1661
 
1395
1662