toil 5.12.0__py3-none-any.whl → 6.1.0a1__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 (157) hide show
  1. toil/__init__.py +18 -13
  2. toil/batchSystems/abstractBatchSystem.py +21 -10
  3. toil/batchSystems/abstractGridEngineBatchSystem.py +2 -2
  4. toil/batchSystems/awsBatch.py +14 -14
  5. toil/batchSystems/contained_executor.py +3 -3
  6. toil/batchSystems/htcondor.py +0 -1
  7. toil/batchSystems/kubernetes.py +34 -31
  8. toil/batchSystems/local_support.py +3 -1
  9. toil/batchSystems/mesos/batchSystem.py +7 -7
  10. toil/batchSystems/options.py +32 -83
  11. toil/batchSystems/registry.py +104 -23
  12. toil/batchSystems/singleMachine.py +16 -13
  13. toil/batchSystems/slurm.py +3 -3
  14. toil/batchSystems/torque.py +0 -1
  15. toil/bus.py +6 -8
  16. toil/common.py +532 -743
  17. toil/cwl/__init__.py +28 -32
  18. toil/cwl/cwltoil.py +523 -520
  19. toil/cwl/utils.py +55 -10
  20. toil/fileStores/__init__.py +2 -2
  21. toil/fileStores/abstractFileStore.py +36 -11
  22. toil/fileStores/cachingFileStore.py +607 -530
  23. toil/fileStores/nonCachingFileStore.py +43 -10
  24. toil/job.py +140 -75
  25. toil/jobStores/abstractJobStore.py +147 -79
  26. toil/jobStores/aws/jobStore.py +23 -9
  27. toil/jobStores/aws/utils.py +1 -2
  28. toil/jobStores/fileJobStore.py +117 -19
  29. toil/jobStores/googleJobStore.py +16 -7
  30. toil/jobStores/utils.py +5 -6
  31. toil/leader.py +71 -43
  32. toil/lib/accelerators.py +10 -5
  33. toil/lib/aws/__init__.py +3 -14
  34. toil/lib/aws/ami.py +22 -9
  35. toil/lib/aws/iam.py +21 -13
  36. toil/lib/aws/session.py +2 -16
  37. toil/lib/aws/utils.py +4 -5
  38. toil/lib/compatibility.py +1 -1
  39. toil/lib/conversions.py +7 -3
  40. toil/lib/docker.py +22 -23
  41. toil/lib/ec2.py +10 -6
  42. toil/lib/ec2nodes.py +106 -100
  43. toil/lib/encryption/_nacl.py +2 -1
  44. toil/lib/generatedEC2Lists.py +325 -18
  45. toil/lib/io.py +21 -0
  46. toil/lib/misc.py +1 -1
  47. toil/lib/resources.py +1 -1
  48. toil/lib/threading.py +74 -26
  49. toil/options/common.py +738 -0
  50. toil/options/cwl.py +336 -0
  51. toil/options/wdl.py +32 -0
  52. toil/provisioners/abstractProvisioner.py +1 -4
  53. toil/provisioners/aws/__init__.py +3 -6
  54. toil/provisioners/aws/awsProvisioner.py +6 -0
  55. toil/provisioners/clusterScaler.py +3 -2
  56. toil/provisioners/gceProvisioner.py +2 -2
  57. toil/realtimeLogger.py +2 -1
  58. toil/resource.py +24 -18
  59. toil/server/app.py +2 -3
  60. toil/server/cli/wes_cwl_runner.py +4 -4
  61. toil/server/utils.py +1 -1
  62. toil/server/wes/abstract_backend.py +3 -2
  63. toil/server/wes/amazon_wes_utils.py +5 -4
  64. toil/server/wes/tasks.py +2 -3
  65. toil/server/wes/toil_backend.py +2 -10
  66. toil/server/wsgi_app.py +2 -0
  67. toil/serviceManager.py +12 -10
  68. toil/statsAndLogging.py +5 -1
  69. toil/test/__init__.py +29 -54
  70. toil/test/batchSystems/batchSystemTest.py +11 -111
  71. toil/test/batchSystems/test_slurm.py +3 -2
  72. toil/test/cwl/cwlTest.py +213 -90
  73. toil/test/cwl/glob_dir.cwl +15 -0
  74. toil/test/cwl/preemptible.cwl +21 -0
  75. toil/test/cwl/preemptible_expression.cwl +28 -0
  76. toil/test/cwl/revsort.cwl +1 -1
  77. toil/test/cwl/revsort2.cwl +1 -1
  78. toil/test/docs/scriptsTest.py +0 -1
  79. toil/test/jobStores/jobStoreTest.py +27 -16
  80. toil/test/lib/aws/test_iam.py +4 -14
  81. toil/test/lib/aws/test_utils.py +0 -3
  82. toil/test/lib/dockerTest.py +4 -4
  83. toil/test/lib/test_ec2.py +11 -16
  84. toil/test/mesos/helloWorld.py +4 -5
  85. toil/test/mesos/stress.py +1 -1
  86. toil/test/provisioners/aws/awsProvisionerTest.py +9 -5
  87. toil/test/provisioners/clusterScalerTest.py +6 -4
  88. toil/test/provisioners/clusterTest.py +14 -3
  89. toil/test/provisioners/gceProvisionerTest.py +0 -6
  90. toil/test/provisioners/restartScript.py +3 -2
  91. toil/test/server/serverTest.py +1 -1
  92. toil/test/sort/restart_sort.py +2 -1
  93. toil/test/sort/sort.py +2 -1
  94. toil/test/sort/sortTest.py +2 -13
  95. toil/test/src/autoDeploymentTest.py +45 -45
  96. toil/test/src/busTest.py +5 -5
  97. toil/test/src/checkpointTest.py +2 -2
  98. toil/test/src/deferredFunctionTest.py +1 -1
  99. toil/test/src/fileStoreTest.py +32 -16
  100. toil/test/src/helloWorldTest.py +1 -1
  101. toil/test/src/importExportFileTest.py +1 -1
  102. toil/test/src/jobDescriptionTest.py +2 -1
  103. toil/test/src/jobServiceTest.py +1 -1
  104. toil/test/src/jobTest.py +18 -18
  105. toil/test/src/miscTests.py +5 -3
  106. toil/test/src/promisedRequirementTest.py +3 -3
  107. toil/test/src/realtimeLoggerTest.py +1 -1
  108. toil/test/src/resourceTest.py +2 -2
  109. toil/test/src/restartDAGTest.py +1 -1
  110. toil/test/src/resumabilityTest.py +36 -2
  111. toil/test/src/retainTempDirTest.py +1 -1
  112. toil/test/src/systemTest.py +2 -2
  113. toil/test/src/toilContextManagerTest.py +2 -2
  114. toil/test/src/userDefinedJobArgTypeTest.py +1 -1
  115. toil/test/utils/toilDebugTest.py +98 -32
  116. toil/test/utils/toilKillTest.py +2 -2
  117. toil/test/utils/utilsTest.py +20 -0
  118. toil/test/wdl/wdltoil_test.py +148 -45
  119. toil/toilState.py +7 -6
  120. toil/utils/toilClean.py +1 -1
  121. toil/utils/toilConfig.py +36 -0
  122. toil/utils/toilDebugFile.py +60 -33
  123. toil/utils/toilDebugJob.py +39 -12
  124. toil/utils/toilDestroyCluster.py +1 -1
  125. toil/utils/toilKill.py +1 -1
  126. toil/utils/toilLaunchCluster.py +13 -2
  127. toil/utils/toilMain.py +3 -2
  128. toil/utils/toilRsyncCluster.py +1 -1
  129. toil/utils/toilSshCluster.py +1 -1
  130. toil/utils/toilStats.py +240 -143
  131. toil/utils/toilStatus.py +1 -4
  132. toil/version.py +11 -11
  133. toil/wdl/utils.py +2 -122
  134. toil/wdl/wdltoil.py +999 -386
  135. toil/worker.py +25 -31
  136. {toil-5.12.0.dist-info → toil-6.1.0a1.dist-info}/METADATA +60 -53
  137. toil-6.1.0a1.dist-info/RECORD +237 -0
  138. {toil-5.12.0.dist-info → toil-6.1.0a1.dist-info}/WHEEL +1 -1
  139. {toil-5.12.0.dist-info → toil-6.1.0a1.dist-info}/entry_points.txt +0 -1
  140. toil/batchSystems/parasol.py +0 -379
  141. toil/batchSystems/tes.py +0 -459
  142. toil/test/batchSystems/parasolTestSupport.py +0 -117
  143. toil/test/wdl/builtinTest.py +0 -506
  144. toil/test/wdl/conftest.py +0 -23
  145. toil/test/wdl/toilwdlTest.py +0 -522
  146. toil/wdl/toilwdl.py +0 -141
  147. toil/wdl/versions/dev.py +0 -107
  148. toil/wdl/versions/draft2.py +0 -980
  149. toil/wdl/versions/v1.py +0 -794
  150. toil/wdl/wdl_analysis.py +0 -116
  151. toil/wdl/wdl_functions.py +0 -997
  152. toil/wdl/wdl_synthesis.py +0 -1011
  153. toil/wdl/wdl_types.py +0 -243
  154. toil-5.12.0.dist-info/RECORD +0 -244
  155. /toil/{wdl/versions → options}/__init__.py +0 -0
  156. {toil-5.12.0.dist-info → toil-6.1.0a1.dist-info}/LICENSE +0 -0
  157. {toil-5.12.0.dist-info → toil-6.1.0a1.dist-info}/top_level.txt +0 -0
toil/cwl/utils.py CHANGED
@@ -16,6 +16,9 @@
16
16
 
17
17
  import logging
18
18
  import os
19
+ from pathlib import PurePosixPath
20
+ import posixpath
21
+ import stat
19
22
  from typing import (
20
23
  Any,
21
24
  Callable,
@@ -24,7 +27,6 @@ from typing import (
24
27
  List,
25
28
  MutableMapping,
26
29
  MutableSequence,
27
- Tuple,
28
30
  Type,
29
31
  TypeVar,
30
32
  Union,
@@ -32,6 +34,7 @@ from typing import (
32
34
 
33
35
  from toil.fileStores import FileID
34
36
  from toil.fileStores.abstractFileStore import AbstractFileStore
37
+ from toil.jobStores.abstractJobStore import AbstractJobStore
35
38
 
36
39
  logger = logging.getLogger(__name__)
37
40
 
@@ -129,6 +132,32 @@ def visit_cwl_class_and_reduce(
129
132
 
130
133
  DirectoryStructure = Dict[str, Union[str, "DirectoryStructure"]]
131
134
 
135
+ def get_from_structure(dir_dict: DirectoryStructure, path: str) -> Union[str, DirectoryStructure, None]:
136
+ """
137
+ Given a relative path, follow it in the given directory structure.
138
+
139
+ Return the string URI for files, the directory dict for
140
+ subdirectories, or None for nonexistent things.
141
+ """
142
+
143
+ # Resolve .. and split into path components
144
+ parts = PurePosixPath(posixpath.normpath(path)).parts
145
+ if len(parts) == 0:
146
+ return dir_dict
147
+ if parts[0] in ('..', '/'):
148
+ raise RuntimeError(f"Path {path} not resolvable in virtual directory")
149
+ found: Union[str, DirectoryStructure] = dir_dict
150
+ for part in parts:
151
+ # Go down by each path component in turn
152
+ if isinstance(found, str):
153
+ # Looking for a subdirectory of a file, which doesn't exist
154
+ return None
155
+ if part not in found:
156
+ return None
157
+ found = found[part]
158
+ # Now we're at the place we want to be.
159
+ return found
160
+
132
161
 
133
162
  def download_structure(
134
163
  file_store: AbstractFileStore,
@@ -140,17 +169,21 @@ def download_structure(
140
169
  """
141
170
  Download nested dictionary from the Toil file store to a local path.
142
171
 
172
+ Guaranteed to fill the structure with real files, and not symlinks out of
173
+ it to elsewhere. File URIs may be toilfile: URIs or any other URI that
174
+ Toil's job store system can read.
175
+
143
176
  :param file_store: The Toil file store to download from.
144
177
 
145
- :param index: Maps from downloaded file path back to input Toil URI.
178
+ :param index: Maps from downloaded file path back to input URI.
146
179
 
147
180
  :param existing: Maps from file_store_id URI to downloaded file path.
148
181
 
149
182
  :param dir_dict: a dict from string to string (for files) or dict (for
150
- subdirectories) describing a directory structure.
183
+ subdirectories) describing a directory structure.
151
184
 
152
185
  :param into_dir: The directory to download the top-level dict's files
153
- into.
186
+ into.
154
187
  """
155
188
  logger.debug("Downloading directory with %s items", len(dir_dict))
156
189
 
@@ -167,14 +200,26 @@ def download_structure(
167
200
  download_structure(file_store, index, existing, value, subdir)
168
201
  else:
169
202
  # This must be a file path uploaded to Toil.
170
- assert isinstance(value, str)
171
- assert value.startswith("toilfile:")
203
+ if not isinstance(value, str):
204
+ raise RuntimeError(f"Did not find a file at {value}.")
205
+
172
206
  logger.debug("Downloading contained file '%s'", name)
173
207
  dest_path = os.path.join(into_dir, name)
174
- # So download the file into place
175
- file_store.readGlobalFile(
176
- FileID.unpack(value[len("toilfile:") :]), dest_path, symlink=True
177
- )
208
+
209
+ if value.startswith("toilfile:"):
210
+ # So download the file into place.
211
+ # Make sure to get a real copy of the file because we may need to
212
+ # mount the directory into a container as a whole.
213
+ file_store.readGlobalFile(
214
+ FileID.unpack(value[len("toilfile:") :]), dest_path, symlink=False
215
+ )
216
+ else:
217
+ # We need to download from some other kind of URL.
218
+ size, executable = AbstractJobStore.read_from_url(value, open(dest_path, 'wb'))
219
+ if executable:
220
+ # Make the written file executable
221
+ os.chmod(dest_path, os.stat(dest_path).st_mode | stat.S_IXUSR)
222
+
178
223
  # Update the index dicts
179
224
  # TODO: why?
180
225
  index[dest_path] = value
@@ -40,7 +40,7 @@ class FileID(str):
40
40
 
41
41
  def pack(self) -> str:
42
42
  """Pack the FileID into a string so it can be passed through external code."""
43
- return f'{self.size}:{int(self.executable)}:{self}'
43
+ return f'{self.size}:{"1" if self.executable else "0"}:{self}'
44
44
 
45
45
  @classmethod
46
46
  def forPath(cls, fileStoreID: str, filePath: str) -> 'FileID':
@@ -54,7 +54,7 @@ class FileID(str):
54
54
  vals = packedFileStoreID.split(':', 2)
55
55
  # Break up the packed value
56
56
  size = int(vals[0])
57
- executable = bool(vals[1])
57
+ executable = (vals[1] == "1")
58
58
  value = vals[2]
59
59
  # Create the FileID
60
60
  return cls(value, size, executable)
@@ -13,9 +13,9 @@
13
13
  # limitations under the License.
14
14
  import logging
15
15
  import os
16
- import tempfile
17
16
  from abc import ABC, abstractmethod
18
17
  from contextlib import contextmanager
18
+ from tempfile import mkstemp
19
19
  from threading import Event, Semaphore
20
20
  from typing import (IO,
21
21
  TYPE_CHECKING,
@@ -26,12 +26,14 @@ from typing import (IO,
26
26
  Generator,
27
27
  Iterator,
28
28
  List,
29
+ Literal,
29
30
  Optional,
30
31
  Set,
31
32
  Tuple,
32
33
  Type,
33
34
  Union,
34
- cast)
35
+ cast,
36
+ overload)
35
37
 
36
38
  import dill
37
39
 
@@ -40,7 +42,7 @@ from toil.fileStores import FileID
40
42
  from toil.job import Job, JobDescription
41
43
  from toil.jobStores.abstractJobStore import AbstractJobStore
42
44
  from toil.lib.compatibility import deprecated
43
- from toil.lib.io import WriteWatchingStream
45
+ from toil.lib.io import WriteWatchingStream, mkdtemp
44
46
 
45
47
  logger = logging.getLogger(__name__)
46
48
 
@@ -120,10 +122,6 @@ class AbstractFileStore(ABC):
120
122
  # job is re-run it will need to be able to re-delete these files.
121
123
  # This is a set of str objects, not FileIDs.
122
124
  self.filesToDelete: Set[str] = set()
123
- # Records IDs of jobs that need to be deleted when the currently
124
- # running job is cleaned up.
125
- # May be modified by the worker to actually delete jobs!
126
- self.jobsToDelete: Set[str] = set()
127
125
  # Holds records of file ID, or file ID and local path, for reporting
128
126
  # the accessed files of failed jobs.
129
127
  self._accessLog: List[Tuple[str, ...]] = []
@@ -211,7 +209,7 @@ class AbstractFileStore(ABC):
211
209
  to be deleted once the job terminates, removing all files it
212
210
  contains recursively.
213
211
  """
214
- return os.path.abspath(tempfile.mkdtemp(dir=self.localTempDir))
212
+ return os.path.abspath(mkdtemp(dir=self.localTempDir))
215
213
 
216
214
  def getLocalTempFile(self, suffix: Optional[str] = None, prefix: Optional[str] = None) -> str:
217
215
  """
@@ -227,7 +225,7 @@ class AbstractFileStore(ABC):
227
225
  for the duration of the job only, and is guaranteed to be deleted
228
226
  once the job terminates.
229
227
  """
230
- handle, tmpFile = tempfile.mkstemp(
228
+ handle, tmpFile = mkstemp(
231
229
  suffix=".tmp" if suffix is None else suffix,
232
230
  prefix="tmp" if prefix is None else prefix,
233
231
  dir=self.localTempDir
@@ -417,6 +415,21 @@ class AbstractFileStore(ABC):
417
415
  """
418
416
  raise NotImplementedError()
419
417
 
418
+ @overload
419
+ def readGlobalFileStream(
420
+ self,
421
+ fileStoreID: str,
422
+ encoding: Literal[None] = None,
423
+ errors: Optional[str] = None,
424
+ ) -> ContextManager[IO[bytes]]:
425
+ ...
426
+
427
+ @overload
428
+ def readGlobalFileStream(
429
+ self, fileStoreID: str, encoding: str, errors: Optional[str] = None
430
+ ) -> ContextManager[IO[str]]:
431
+ ...
432
+
420
433
  @abstractmethod
421
434
  def readGlobalFileStream(
422
435
  self,
@@ -589,7 +602,7 @@ class AbstractFileStore(ABC):
589
602
  os.rename(fileName + '.tmp', fileName)
590
603
 
591
604
  # Functions related to logging
592
- def logToMaster(self, text: str, level: int = logging.INFO) -> None:
605
+ def log_to_leader(self, text: str, level: int = logging.INFO) -> None:
593
606
  """
594
607
  Send a logging message to the leader. The message will also be \
595
608
  logged by the worker at the same level.
@@ -600,13 +613,25 @@ class AbstractFileStore(ABC):
600
613
  logger.log(level=level, msg=("LOG-TO-MASTER: " + text))
601
614
  self.loggingMessages.append(dict(text=text, level=level))
602
615
 
616
+
617
+ @deprecated(new_function_name='export_file')
618
+ def logToMaster(self, text: str, level: int = logging.INFO) -> None:
619
+ self.log_to_leader(text, level)
620
+
621
+
603
622
  # Functions run after the completion of the job.
604
623
  @abstractmethod
605
624
  def startCommit(self, jobState: bool = False) -> None:
606
625
  """
607
626
  Update the status of the job on the disk.
608
627
 
609
- May start an asynchronous process. Call waitForCommit() to wait on that process.
628
+ May bump the version number of the job.
629
+
630
+ May start an asynchronous process. Call waitForCommit() to wait on that
631
+ process. You must waitForCommit() before committing any further updates
632
+ to the job. During the asynchronous process, it is safe to modify the
633
+ job; modifications after this call will not be committed until the next
634
+ call.
610
635
 
611
636
  :param jobState: If True, commit the state of the FileStore's job,
612
637
  and file deletes. Otherwise, commit only file creates/updates.