toil 7.0.0__py3-none-any.whl → 8.1.0b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- toil/__init__.py +124 -86
- toil/batchSystems/__init__.py +1 -0
- toil/batchSystems/abstractBatchSystem.py +137 -77
- toil/batchSystems/abstractGridEngineBatchSystem.py +211 -101
- toil/batchSystems/awsBatch.py +237 -128
- toil/batchSystems/cleanup_support.py +22 -16
- toil/batchSystems/contained_executor.py +30 -26
- toil/batchSystems/gridengine.py +85 -49
- toil/batchSystems/htcondor.py +164 -87
- toil/batchSystems/kubernetes.py +622 -386
- toil/batchSystems/local_support.py +17 -12
- toil/batchSystems/lsf.py +132 -79
- toil/batchSystems/lsfHelper.py +13 -11
- toil/batchSystems/mesos/__init__.py +41 -29
- toil/batchSystems/mesos/batchSystem.py +288 -149
- toil/batchSystems/mesos/executor.py +77 -49
- toil/batchSystems/mesos/test/__init__.py +31 -23
- toil/batchSystems/options.py +39 -29
- toil/batchSystems/registry.py +53 -19
- toil/batchSystems/singleMachine.py +293 -123
- toil/batchSystems/slurm.py +651 -155
- toil/batchSystems/torque.py +46 -32
- toil/bus.py +141 -73
- toil/common.py +784 -397
- toil/cwl/__init__.py +1 -1
- toil/cwl/cwltoil.py +1137 -534
- toil/cwl/utils.py +17 -22
- toil/deferred.py +62 -41
- toil/exceptions.py +5 -3
- toil/fileStores/__init__.py +5 -5
- toil/fileStores/abstractFileStore.py +88 -57
- toil/fileStores/cachingFileStore.py +711 -247
- toil/fileStores/nonCachingFileStore.py +113 -75
- toil/job.py +1031 -349
- toil/jobStores/abstractJobStore.py +387 -243
- toil/jobStores/aws/jobStore.py +772 -412
- toil/jobStores/aws/utils.py +161 -109
- toil/jobStores/conftest.py +1 -0
- toil/jobStores/fileJobStore.py +289 -151
- toil/jobStores/googleJobStore.py +137 -70
- toil/jobStores/utils.py +36 -15
- toil/leader.py +614 -269
- toil/lib/accelerators.py +115 -18
- toil/lib/aws/__init__.py +55 -28
- toil/lib/aws/ami.py +122 -87
- toil/lib/aws/iam.py +284 -108
- toil/lib/aws/s3.py +31 -0
- toil/lib/aws/session.py +204 -58
- toil/lib/aws/utils.py +290 -213
- toil/lib/bioio.py +13 -5
- toil/lib/compatibility.py +11 -6
- toil/lib/conversions.py +83 -49
- toil/lib/docker.py +131 -103
- toil/lib/dockstore.py +379 -0
- toil/lib/ec2.py +322 -209
- toil/lib/ec2nodes.py +174 -105
- toil/lib/encryption/_dummy.py +5 -3
- toil/lib/encryption/_nacl.py +10 -6
- toil/lib/encryption/conftest.py +1 -0
- toil/lib/exceptions.py +26 -7
- toil/lib/expando.py +4 -2
- toil/lib/ftp_utils.py +217 -0
- toil/lib/generatedEC2Lists.py +127 -19
- toil/lib/history.py +1271 -0
- toil/lib/history_submission.py +681 -0
- toil/lib/humanize.py +6 -2
- toil/lib/io.py +121 -12
- toil/lib/iterables.py +4 -2
- toil/lib/memoize.py +12 -8
- toil/lib/misc.py +83 -18
- toil/lib/objects.py +2 -2
- toil/lib/resources.py +19 -7
- toil/lib/retry.py +125 -87
- toil/lib/threading.py +282 -80
- toil/lib/throttle.py +15 -14
- toil/lib/trs.py +390 -0
- toil/lib/web.py +38 -0
- toil/options/common.py +850 -402
- toil/options/cwl.py +185 -90
- toil/options/runner.py +50 -0
- toil/options/wdl.py +70 -19
- toil/provisioners/__init__.py +111 -46
- toil/provisioners/abstractProvisioner.py +322 -157
- toil/provisioners/aws/__init__.py +62 -30
- toil/provisioners/aws/awsProvisioner.py +980 -627
- toil/provisioners/clusterScaler.py +541 -279
- toil/provisioners/gceProvisioner.py +283 -180
- toil/provisioners/node.py +147 -79
- toil/realtimeLogger.py +34 -22
- toil/resource.py +137 -75
- toil/server/app.py +127 -61
- toil/server/celery_app.py +3 -1
- toil/server/cli/wes_cwl_runner.py +84 -55
- toil/server/utils.py +56 -31
- toil/server/wes/abstract_backend.py +64 -26
- toil/server/wes/amazon_wes_utils.py +21 -15
- toil/server/wes/tasks.py +121 -63
- toil/server/wes/toil_backend.py +142 -107
- toil/server/wsgi_app.py +4 -3
- toil/serviceManager.py +58 -22
- toil/statsAndLogging.py +183 -65
- toil/test/__init__.py +263 -179
- toil/test/batchSystems/batchSystemTest.py +438 -195
- toil/test/batchSystems/batch_system_plugin_test.py +18 -7
- toil/test/batchSystems/test_gridengine.py +173 -0
- toil/test/batchSystems/test_lsf_helper.py +67 -58
- toil/test/batchSystems/test_slurm.py +265 -49
- toil/test/cactus/test_cactus_integration.py +20 -22
- toil/test/cwl/conftest.py +39 -0
- toil/test/cwl/cwlTest.py +375 -72
- toil/test/cwl/measure_default_memory.cwl +12 -0
- toil/test/cwl/not_run_required_input.cwl +29 -0
- toil/test/cwl/optional-file.cwl +18 -0
- toil/test/cwl/scatter_duplicate_outputs.cwl +40 -0
- toil/test/docs/scriptsTest.py +60 -34
- toil/test/jobStores/jobStoreTest.py +412 -235
- toil/test/lib/aws/test_iam.py +116 -48
- toil/test/lib/aws/test_s3.py +16 -9
- toil/test/lib/aws/test_utils.py +5 -6
- toil/test/lib/dockerTest.py +118 -141
- toil/test/lib/test_conversions.py +113 -115
- toil/test/lib/test_ec2.py +57 -49
- toil/test/lib/test_history.py +212 -0
- toil/test/lib/test_misc.py +12 -5
- toil/test/lib/test_trs.py +161 -0
- toil/test/mesos/MesosDataStructuresTest.py +23 -10
- toil/test/mesos/helloWorld.py +7 -6
- toil/test/mesos/stress.py +25 -20
- toil/test/options/options.py +7 -2
- toil/test/provisioners/aws/awsProvisionerTest.py +293 -140
- toil/test/provisioners/clusterScalerTest.py +440 -250
- toil/test/provisioners/clusterTest.py +81 -42
- toil/test/provisioners/gceProvisionerTest.py +174 -100
- toil/test/provisioners/provisionerTest.py +25 -13
- toil/test/provisioners/restartScript.py +5 -4
- toil/test/server/serverTest.py +188 -141
- toil/test/sort/restart_sort.py +137 -68
- toil/test/sort/sort.py +134 -66
- toil/test/sort/sortTest.py +91 -49
- toil/test/src/autoDeploymentTest.py +140 -100
- toil/test/src/busTest.py +20 -18
- toil/test/src/checkpointTest.py +8 -2
- toil/test/src/deferredFunctionTest.py +49 -35
- toil/test/src/dockerCheckTest.py +33 -26
- toil/test/src/environmentTest.py +20 -10
- toil/test/src/fileStoreTest.py +538 -271
- toil/test/src/helloWorldTest.py +7 -4
- toil/test/src/importExportFileTest.py +61 -31
- toil/test/src/jobDescriptionTest.py +32 -17
- toil/test/src/jobEncapsulationTest.py +2 -0
- toil/test/src/jobFileStoreTest.py +74 -50
- toil/test/src/jobServiceTest.py +187 -73
- toil/test/src/jobTest.py +120 -70
- toil/test/src/miscTests.py +19 -18
- toil/test/src/promisedRequirementTest.py +82 -36
- toil/test/src/promisesTest.py +7 -6
- toil/test/src/realtimeLoggerTest.py +6 -6
- toil/test/src/regularLogTest.py +71 -37
- toil/test/src/resourceTest.py +80 -49
- toil/test/src/restartDAGTest.py +36 -22
- toil/test/src/resumabilityTest.py +9 -2
- toil/test/src/retainTempDirTest.py +45 -14
- toil/test/src/systemTest.py +12 -8
- toil/test/src/threadingTest.py +44 -25
- toil/test/src/toilContextManagerTest.py +10 -7
- toil/test/src/userDefinedJobArgTypeTest.py +8 -5
- toil/test/src/workerTest.py +33 -16
- toil/test/utils/toilDebugTest.py +70 -58
- toil/test/utils/toilKillTest.py +4 -5
- toil/test/utils/utilsTest.py +239 -102
- toil/test/wdl/wdltoil_test.py +789 -148
- toil/test/wdl/wdltoil_test_kubernetes.py +37 -23
- toil/toilState.py +52 -26
- toil/utils/toilConfig.py +13 -4
- toil/utils/toilDebugFile.py +44 -27
- toil/utils/toilDebugJob.py +85 -25
- toil/utils/toilDestroyCluster.py +11 -6
- toil/utils/toilKill.py +8 -3
- toil/utils/toilLaunchCluster.py +251 -145
- toil/utils/toilMain.py +37 -16
- toil/utils/toilRsyncCluster.py +27 -14
- toil/utils/toilSshCluster.py +45 -22
- toil/utils/toilStats.py +75 -36
- toil/utils/toilStatus.py +226 -119
- toil/utils/toilUpdateEC2Instances.py +3 -1
- toil/version.py +6 -6
- toil/wdl/utils.py +5 -5
- toil/wdl/wdltoil.py +3528 -1053
- toil/worker.py +370 -149
- toil-8.1.0b1.dist-info/METADATA +178 -0
- toil-8.1.0b1.dist-info/RECORD +259 -0
- {toil-7.0.0.dist-info → toil-8.1.0b1.dist-info}/WHEEL +1 -1
- toil-7.0.0.dist-info/METADATA +0 -158
- toil-7.0.0.dist-info/RECORD +0 -244
- {toil-7.0.0.dist-info → toil-8.1.0b1.dist-info}/LICENSE +0 -0
- {toil-7.0.0.dist-info → toil-8.1.0b1.dist-info}/entry_points.txt +0 -0
- {toil-7.0.0.dist-info → toil-8.1.0b1.dist-info}/top_level.txt +0 -0
|
@@ -14,32 +14,28 @@
|
|
|
14
14
|
import logging
|
|
15
15
|
import os
|
|
16
16
|
from abc import ABC, abstractmethod
|
|
17
|
+
from collections.abc import Generator, Iterator
|
|
17
18
|
from contextlib import contextmanager
|
|
18
19
|
from tempfile import mkstemp
|
|
19
20
|
from threading import Event, Semaphore
|
|
20
|
-
from typing import (
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
Tuple,
|
|
33
|
-
Type,
|
|
34
|
-
Union,
|
|
35
|
-
cast,
|
|
36
|
-
overload)
|
|
21
|
+
from typing import (
|
|
22
|
+
IO,
|
|
23
|
+
TYPE_CHECKING,
|
|
24
|
+
Any,
|
|
25
|
+
Callable,
|
|
26
|
+
ContextManager,
|
|
27
|
+
Literal,
|
|
28
|
+
Optional,
|
|
29
|
+
Union,
|
|
30
|
+
cast,
|
|
31
|
+
overload,
|
|
32
|
+
)
|
|
37
33
|
|
|
38
34
|
import dill
|
|
39
35
|
|
|
40
36
|
from toil.common import Toil, cacheDirName, getDirSizeRecursively
|
|
41
37
|
from toil.fileStores import FileID
|
|
42
|
-
from toil.job import Job, JobDescription
|
|
38
|
+
from toil.job import DebugStoppingPointReached, Job, JobDescription
|
|
43
39
|
from toil.jobStores.abstractJobStore import AbstractJobStore
|
|
44
40
|
from toil.lib.compatibility import deprecated
|
|
45
41
|
from toil.lib.conversions import bytes2human
|
|
@@ -76,9 +72,10 @@ class AbstractFileStore(ABC):
|
|
|
76
72
|
Also responsible for committing completed jobs back to the job store with
|
|
77
73
|
an update operation, and allowing that commit operation to be waited for.
|
|
78
74
|
"""
|
|
75
|
+
|
|
79
76
|
# Variables used for syncing reads/writes
|
|
80
77
|
_pendingFileWritesLock = Semaphore()
|
|
81
|
-
_pendingFileWrites:
|
|
78
|
+
_pendingFileWrites: set[str] = set()
|
|
82
79
|
_terminateEvent = Event() # Used to signify crashes in threads
|
|
83
80
|
|
|
84
81
|
def __init__(
|
|
@@ -111,20 +108,26 @@ class AbstractFileStore(ABC):
|
|
|
111
108
|
# This gets replaced with a subdirectory of itself on open()
|
|
112
109
|
self.localTempDir: str = os.path.abspath(file_store_dir)
|
|
113
110
|
assert self.jobStore.config.workflowID is not None
|
|
114
|
-
self.workflow_dir: str = Toil.getLocalWorkflowDir(
|
|
115
|
-
|
|
111
|
+
self.workflow_dir: str = Toil.getLocalWorkflowDir(
|
|
112
|
+
self.jobStore.config.workflowID, self.jobStore.config.workDir
|
|
113
|
+
)
|
|
114
|
+
self.coordination_dir: str = Toil.get_local_workflow_coordination_dir(
|
|
115
|
+
self.jobStore.config.workflowID,
|
|
116
|
+
self.jobStore.config.workDir,
|
|
117
|
+
self.jobStore.config.coordination_dir,
|
|
118
|
+
)
|
|
116
119
|
self.jobName: str = str(self.jobDesc)
|
|
117
120
|
self.waitForPreviousCommit = waitForPreviousCommit
|
|
118
|
-
self.logging_messages:
|
|
119
|
-
self.logging_user_streams:
|
|
121
|
+
self.logging_messages: list[dict[str, Union[int, str]]] = []
|
|
122
|
+
self.logging_user_streams: list[dict[str, str]] = []
|
|
120
123
|
# Records file IDs of files deleted during the current job. Doesn't get
|
|
121
124
|
# committed back until the job is completely successful, because if the
|
|
122
125
|
# job is re-run it will need to be able to re-delete these files.
|
|
123
126
|
# This is a set of str objects, not FileIDs.
|
|
124
|
-
self.filesToDelete:
|
|
127
|
+
self.filesToDelete: set[str] = set()
|
|
125
128
|
# Holds records of file ID, or file ID and local path, for reporting
|
|
126
129
|
# the accessed files of failed jobs.
|
|
127
|
-
self._accessLog:
|
|
130
|
+
self._accessLog: list[tuple[str, ...]] = []
|
|
128
131
|
# Holds total bytes of observed disk usage for the last job run under open()
|
|
129
132
|
self._job_disk_used: Optional[int] = None
|
|
130
133
|
|
|
@@ -141,13 +144,17 @@ class AbstractFileStore(ABC):
|
|
|
141
144
|
from toil.fileStores.cachingFileStore import CachingFileStore
|
|
142
145
|
from toil.fileStores.nonCachingFileStore import NonCachingFileStore
|
|
143
146
|
|
|
144
|
-
fileStoreCls: Union[
|
|
147
|
+
fileStoreCls: Union[type["CachingFileStore"], type["NonCachingFileStore"]] = (
|
|
145
148
|
CachingFileStore if caching else NonCachingFileStore
|
|
146
149
|
)
|
|
147
150
|
return fileStoreCls(jobStore, jobDesc, file_store_dir, waitForPreviousCommit)
|
|
148
151
|
|
|
149
152
|
@staticmethod
|
|
150
|
-
def shutdownFileStore(
|
|
153
|
+
def shutdownFileStore(
|
|
154
|
+
workflowID: str,
|
|
155
|
+
config_work_dir: Optional[str],
|
|
156
|
+
config_coordination_dir: Optional[str],
|
|
157
|
+
) -> None:
|
|
151
158
|
"""
|
|
152
159
|
Carry out any necessary filestore-specific cleanup.
|
|
153
160
|
|
|
@@ -167,7 +174,9 @@ class AbstractFileStore(ABC):
|
|
|
167
174
|
from toil.fileStores.nonCachingFileStore import NonCachingFileStore
|
|
168
175
|
|
|
169
176
|
workflowDir = Toil.getLocalWorkflowDir(workflowID, config_work_dir)
|
|
170
|
-
coordination_dir = Toil.get_local_workflow_coordination_dir(
|
|
177
|
+
coordination_dir = Toil.get_local_workflow_coordination_dir(
|
|
178
|
+
workflowID, config_work_dir, config_coordination_dir
|
|
179
|
+
)
|
|
171
180
|
cacheDir = os.path.join(workflowDir, cacheDirName(workflowID))
|
|
172
181
|
if os.path.exists(cacheDir):
|
|
173
182
|
# The presence of the cacheDir suggests this was a cached run. We don't need
|
|
@@ -208,12 +217,16 @@ class AbstractFileStore(ABC):
|
|
|
208
217
|
percent: float = 0.0
|
|
209
218
|
if job_requested_disk and job_requested_disk > 0:
|
|
210
219
|
percent = float(self._job_disk_used) / job_requested_disk * 100
|
|
211
|
-
disk_usage: str = (
|
|
212
|
-
|
|
220
|
+
disk_usage: str = (
|
|
221
|
+
f"Job {self.jobName} used {percent:.2f}% disk ({bytes2human(self._job_disk_used)}B [{self._job_disk_used}B] used, "
|
|
222
|
+
f"{bytes2human(job_requested_disk)}B [{job_requested_disk}B] requested)."
|
|
223
|
+
)
|
|
213
224
|
if self._job_disk_used > job_requested_disk:
|
|
214
|
-
self.log_to_leader(
|
|
215
|
-
|
|
216
|
-
|
|
225
|
+
self.log_to_leader(
|
|
226
|
+
"Job used more disk than requested. For CWL, consider increasing the outdirMin "
|
|
227
|
+
f"requirement, otherwise, consider increasing the disk requirement. {disk_usage}",
|
|
228
|
+
level=logging.WARNING,
|
|
229
|
+
)
|
|
217
230
|
else:
|
|
218
231
|
self.log_to_leader(disk_usage, level=logging.DEBUG)
|
|
219
232
|
|
|
@@ -226,7 +239,6 @@ class AbstractFileStore(ABC):
|
|
|
226
239
|
"""
|
|
227
240
|
return self._job_disk_used
|
|
228
241
|
|
|
229
|
-
|
|
230
242
|
# Functions related to temp files and directories
|
|
231
243
|
def getLocalTempDir(self) -> str:
|
|
232
244
|
"""
|
|
@@ -241,7 +253,9 @@ class AbstractFileStore(ABC):
|
|
|
241
253
|
"""
|
|
242
254
|
return os.path.abspath(mkdtemp(dir=self.localTempDir))
|
|
243
255
|
|
|
244
|
-
def getLocalTempFile(
|
|
256
|
+
def getLocalTempFile(
|
|
257
|
+
self, suffix: Optional[str] = None, prefix: Optional[str] = None
|
|
258
|
+
) -> str:
|
|
245
259
|
"""
|
|
246
260
|
Get a new local temporary file that will persist for the duration of the job.
|
|
247
261
|
|
|
@@ -258,12 +272,14 @@ class AbstractFileStore(ABC):
|
|
|
258
272
|
handle, tmpFile = mkstemp(
|
|
259
273
|
suffix=".tmp" if suffix is None else suffix,
|
|
260
274
|
prefix="tmp" if prefix is None else prefix,
|
|
261
|
-
dir=self.localTempDir
|
|
275
|
+
dir=self.localTempDir,
|
|
262
276
|
)
|
|
263
277
|
os.close(handle)
|
|
264
278
|
return os.path.abspath(tmpFile)
|
|
265
279
|
|
|
266
|
-
def getLocalTempFileName(
|
|
280
|
+
def getLocalTempFileName(
|
|
281
|
+
self, suffix: Optional[str] = None, prefix: Optional[str] = None
|
|
282
|
+
) -> str:
|
|
267
283
|
"""
|
|
268
284
|
Get a valid name for a new local file. Don't actually create a file at the path.
|
|
269
285
|
|
|
@@ -317,7 +333,7 @@ class AbstractFileStore(ABC):
|
|
|
317
333
|
basename: Optional[str] = None,
|
|
318
334
|
encoding: Optional[str] = None,
|
|
319
335
|
errors: Optional[str] = None,
|
|
320
|
-
) -> Iterator[
|
|
336
|
+
) -> Iterator[tuple[WriteWatchingStream, FileID]]:
|
|
321
337
|
"""
|
|
322
338
|
Similar to writeGlobalFile, but allows the writing of a stream to the job store.
|
|
323
339
|
The yielded file handle does not need to and should not be closed explicitly.
|
|
@@ -357,11 +373,14 @@ class AbstractFileStore(ABC):
|
|
|
357
373
|
def handle(numBytes: int) -> None:
|
|
358
374
|
# No scope problem here, because we don't assign to a fileID local
|
|
359
375
|
fileID.size += numBytes
|
|
376
|
+
|
|
360
377
|
wrappedStream.onWrite(handle)
|
|
361
378
|
|
|
362
379
|
yield wrappedStream, fileID
|
|
363
380
|
|
|
364
|
-
def _dumpAccessLogs(
|
|
381
|
+
def _dumpAccessLogs(
|
|
382
|
+
self, job_type: str = "Failed", log_level: int = logging.WARNING
|
|
383
|
+
) -> None:
|
|
365
384
|
"""
|
|
366
385
|
Log a report of the files accessed.
|
|
367
386
|
|
|
@@ -370,7 +389,7 @@ class AbstractFileStore(ABC):
|
|
|
370
389
|
:param job_type: Adjective to describe the job in the report.
|
|
371
390
|
"""
|
|
372
391
|
if len(self._accessLog) > 0:
|
|
373
|
-
logger.log(log_level,
|
|
392
|
+
logger.log(log_level, "%s job accessed files:", job_type)
|
|
374
393
|
|
|
375
394
|
for item in self._accessLog:
|
|
376
395
|
# For each access record
|
|
@@ -379,14 +398,29 @@ class AbstractFileStore(ABC):
|
|
|
379
398
|
file_id, dest_path = item
|
|
380
399
|
if os.path.exists(dest_path):
|
|
381
400
|
if os.path.islink(dest_path):
|
|
382
|
-
logger.log(
|
|
401
|
+
logger.log(
|
|
402
|
+
log_level,
|
|
403
|
+
"Symlinked file '%s' to path '%s'",
|
|
404
|
+
file_id,
|
|
405
|
+
dest_path,
|
|
406
|
+
)
|
|
383
407
|
else:
|
|
384
|
-
logger.log(
|
|
408
|
+
logger.log(
|
|
409
|
+
log_level,
|
|
410
|
+
"Downloaded file '%s' to path '%s'",
|
|
411
|
+
file_id,
|
|
412
|
+
dest_path,
|
|
413
|
+
)
|
|
385
414
|
else:
|
|
386
|
-
logger.log(
|
|
415
|
+
logger.log(
|
|
416
|
+
log_level,
|
|
417
|
+
"Downloaded file '%s' to path '%s' (gone!)",
|
|
418
|
+
file_id,
|
|
419
|
+
dest_path,
|
|
420
|
+
)
|
|
387
421
|
else:
|
|
388
422
|
# Otherwise dump without the name
|
|
389
|
-
logger.log(log_level,
|
|
423
|
+
logger.log(log_level, "Streamed file '%s'", *item)
|
|
390
424
|
|
|
391
425
|
def logAccess(
|
|
392
426
|
self, fileStoreID: Union[FileID, str], destination: Union[str, None] = None
|
|
@@ -453,14 +487,12 @@ class AbstractFileStore(ABC):
|
|
|
453
487
|
fileStoreID: str,
|
|
454
488
|
encoding: Literal[None] = None,
|
|
455
489
|
errors: Optional[str] = None,
|
|
456
|
-
) -> ContextManager[IO[bytes]]:
|
|
457
|
-
...
|
|
490
|
+
) -> ContextManager[IO[bytes]]: ...
|
|
458
491
|
|
|
459
492
|
@overload
|
|
460
493
|
def readGlobalFileStream(
|
|
461
494
|
self, fileStoreID: str, encoding: str, errors: Optional[str] = None
|
|
462
|
-
) -> ContextManager[IO[str]]:
|
|
463
|
-
...
|
|
495
|
+
) -> ContextManager[IO[str]]: ...
|
|
464
496
|
|
|
465
497
|
@abstractmethod
|
|
466
498
|
def readGlobalFileStream(
|
|
@@ -504,7 +536,7 @@ class AbstractFileStore(ABC):
|
|
|
504
536
|
:return: File's size in bytes, as stored in the job store
|
|
505
537
|
"""
|
|
506
538
|
# First try and see if the size is still attached
|
|
507
|
-
size = getattr(fileStoreID,
|
|
539
|
+
size = getattr(fileStoreID, "size", None)
|
|
508
540
|
|
|
509
541
|
if size is None:
|
|
510
542
|
# It fell off
|
|
@@ -557,7 +589,7 @@ class AbstractFileStore(ABC):
|
|
|
557
589
|
) -> Optional[FileID]:
|
|
558
590
|
return self.jobStore.import_file(src_uri, shared_file_name=shared_file_name)
|
|
559
591
|
|
|
560
|
-
@deprecated(new_function_name=
|
|
592
|
+
@deprecated(new_function_name="export_file")
|
|
561
593
|
def exportFile(self, jobStoreFileID: FileID, dstUrl: str) -> None:
|
|
562
594
|
return self.export_file(jobStoreFileID, dstUrl)
|
|
563
595
|
|
|
@@ -586,7 +618,7 @@ class AbstractFileStore(ABC):
|
|
|
586
618
|
class _StateFile:
|
|
587
619
|
"""Read and write dill-ed state dictionaries from/to a file into a namespace."""
|
|
588
620
|
|
|
589
|
-
def __init__(self, stateDict:
|
|
621
|
+
def __init__(self, stateDict: dict[str, Any]):
|
|
590
622
|
assert isinstance(stateDict, dict)
|
|
591
623
|
self.__dict__.update(stateDict)
|
|
592
624
|
|
|
@@ -614,7 +646,7 @@ class AbstractFileStore(ABC):
|
|
|
614
646
|
"""
|
|
615
647
|
# Read the value from the cache state file then initialize and instance of
|
|
616
648
|
# _CacheState with it.
|
|
617
|
-
with open(fileName,
|
|
649
|
+
with open(fileName, "rb") as fH:
|
|
618
650
|
infoDict = dill.load(fH)
|
|
619
651
|
return cls(infoDict)
|
|
620
652
|
|
|
@@ -624,14 +656,14 @@ class AbstractFileStore(ABC):
|
|
|
624
656
|
|
|
625
657
|
:param fileName: Path to the state file.
|
|
626
658
|
"""
|
|
627
|
-
with open(fileName +
|
|
659
|
+
with open(fileName + ".tmp", "wb") as fH:
|
|
628
660
|
# Based on answer by user "Mark" at:
|
|
629
661
|
# http://stackoverflow.com/questions/2709800/how-to-pickle-yourself
|
|
630
662
|
# We can't pickle nested classes. So we have to pickle the variables
|
|
631
663
|
# of the class.
|
|
632
664
|
# If we ever change this, we need to ensure it doesn't break FileID
|
|
633
665
|
dill.dump(self.__dict__, fH)
|
|
634
|
-
os.rename(fileName +
|
|
666
|
+
os.rename(fileName + ".tmp", fileName)
|
|
635
667
|
|
|
636
668
|
# Functions related to logging
|
|
637
669
|
def log_to_leader(self, text: str, level: int = logging.INFO) -> None:
|
|
@@ -645,8 +677,7 @@ class AbstractFileStore(ABC):
|
|
|
645
677
|
logger.log(level=level, msg=("LOG-TO-MASTER: " + text))
|
|
646
678
|
self.logging_messages.append(dict(text=text, level=level))
|
|
647
679
|
|
|
648
|
-
|
|
649
|
-
@deprecated(new_function_name='export_file')
|
|
680
|
+
@deprecated(new_function_name="export_file")
|
|
650
681
|
def logToMaster(self, text: str, level: int = logging.INFO) -> None:
|
|
651
682
|
self.log_to_leader(text, level)
|
|
652
683
|
|
|
@@ -664,7 +695,7 @@ class AbstractFileStore(ABC):
|
|
|
664
695
|
"""
|
|
665
696
|
|
|
666
697
|
# Read the whole stream into memory
|
|
667
|
-
steam_data = stream.read().decode(
|
|
698
|
+
steam_data = stream.read().decode("utf-8", errors="replace")
|
|
668
699
|
# And remember it for the worker to fish out
|
|
669
700
|
self.logging_user_streams.append(dict(name=name, text=steam_data))
|
|
670
701
|
|