toil 9.1.1__py3-none-any.whl → 9.2.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 (155) hide show
  1. toil/__init__.py +5 -9
  2. toil/batchSystems/abstractBatchSystem.py +23 -22
  3. toil/batchSystems/abstractGridEngineBatchSystem.py +17 -12
  4. toil/batchSystems/awsBatch.py +8 -8
  5. toil/batchSystems/cleanup_support.py +4 -4
  6. toil/batchSystems/contained_executor.py +3 -3
  7. toil/batchSystems/gridengine.py +3 -4
  8. toil/batchSystems/htcondor.py +5 -5
  9. toil/batchSystems/kubernetes.py +65 -63
  10. toil/batchSystems/local_support.py +2 -3
  11. toil/batchSystems/lsf.py +6 -7
  12. toil/batchSystems/mesos/batchSystem.py +11 -7
  13. toil/batchSystems/mesos/test/__init__.py +1 -2
  14. toil/batchSystems/options.py +9 -10
  15. toil/batchSystems/registry.py +3 -7
  16. toil/batchSystems/singleMachine.py +8 -11
  17. toil/batchSystems/slurm.py +49 -38
  18. toil/batchSystems/torque.py +3 -4
  19. toil/bus.py +36 -34
  20. toil/common.py +129 -89
  21. toil/cwl/cwltoil.py +857 -729
  22. toil/cwl/utils.py +44 -35
  23. toil/fileStores/__init__.py +3 -1
  24. toil/fileStores/abstractFileStore.py +28 -30
  25. toil/fileStores/cachingFileStore.py +8 -8
  26. toil/fileStores/nonCachingFileStore.py +10 -21
  27. toil/job.py +159 -158
  28. toil/jobStores/abstractJobStore.py +68 -69
  29. toil/jobStores/aws/jobStore.py +249 -213
  30. toil/jobStores/aws/utils.py +13 -24
  31. toil/jobStores/fileJobStore.py +28 -22
  32. toil/jobStores/googleJobStore.py +21 -17
  33. toil/jobStores/utils.py +3 -7
  34. toil/leader.py +17 -22
  35. toil/lib/accelerators.py +6 -4
  36. toil/lib/aws/__init__.py +9 -10
  37. toil/lib/aws/ami.py +33 -19
  38. toil/lib/aws/iam.py +6 -6
  39. toil/lib/aws/s3.py +259 -157
  40. toil/lib/aws/session.py +76 -76
  41. toil/lib/aws/utils.py +51 -43
  42. toil/lib/checksum.py +19 -15
  43. toil/lib/compatibility.py +3 -2
  44. toil/lib/conversions.py +45 -18
  45. toil/lib/directory.py +29 -26
  46. toil/lib/docker.py +93 -99
  47. toil/lib/dockstore.py +77 -50
  48. toil/lib/ec2.py +39 -38
  49. toil/lib/ec2nodes.py +11 -4
  50. toil/lib/exceptions.py +8 -5
  51. toil/lib/ftp_utils.py +9 -14
  52. toil/lib/generatedEC2Lists.py +161 -20
  53. toil/lib/history.py +141 -97
  54. toil/lib/history_submission.py +163 -72
  55. toil/lib/io.py +27 -17
  56. toil/lib/memoize.py +2 -1
  57. toil/lib/misc.py +15 -11
  58. toil/lib/pipes.py +40 -25
  59. toil/lib/plugins.py +12 -8
  60. toil/lib/resources.py +1 -0
  61. toil/lib/retry.py +32 -38
  62. toil/lib/threading.py +12 -12
  63. toil/lib/throttle.py +1 -2
  64. toil/lib/trs.py +113 -51
  65. toil/lib/url.py +14 -23
  66. toil/lib/web.py +7 -2
  67. toil/options/common.py +18 -15
  68. toil/options/cwl.py +2 -2
  69. toil/options/runner.py +9 -5
  70. toil/options/wdl.py +1 -3
  71. toil/provisioners/__init__.py +9 -9
  72. toil/provisioners/abstractProvisioner.py +22 -20
  73. toil/provisioners/aws/__init__.py +20 -14
  74. toil/provisioners/aws/awsProvisioner.py +10 -8
  75. toil/provisioners/clusterScaler.py +19 -18
  76. toil/provisioners/gceProvisioner.py +2 -3
  77. toil/provisioners/node.py +11 -13
  78. toil/realtimeLogger.py +4 -4
  79. toil/resource.py +5 -5
  80. toil/server/app.py +2 -2
  81. toil/server/cli/wes_cwl_runner.py +11 -11
  82. toil/server/utils.py +18 -21
  83. toil/server/wes/abstract_backend.py +9 -8
  84. toil/server/wes/amazon_wes_utils.py +3 -3
  85. toil/server/wes/tasks.py +3 -5
  86. toil/server/wes/toil_backend.py +17 -21
  87. toil/server/wsgi_app.py +3 -3
  88. toil/serviceManager.py +3 -4
  89. toil/statsAndLogging.py +12 -13
  90. toil/test/__init__.py +33 -24
  91. toil/test/batchSystems/batchSystemTest.py +12 -11
  92. toil/test/batchSystems/batch_system_plugin_test.py +3 -5
  93. toil/test/batchSystems/test_slurm.py +38 -24
  94. toil/test/cwl/conftest.py +5 -6
  95. toil/test/cwl/cwlTest.py +194 -78
  96. toil/test/cwl/download_file_uri.json +6 -0
  97. toil/test/cwl/download_file_uri_no_hostname.json +6 -0
  98. toil/test/docs/scripts/tutorial_staging.py +1 -0
  99. toil/test/jobStores/jobStoreTest.py +9 -7
  100. toil/test/lib/aws/test_iam.py +1 -3
  101. toil/test/lib/aws/test_s3.py +1 -1
  102. toil/test/lib/dockerTest.py +9 -9
  103. toil/test/lib/test_ec2.py +12 -11
  104. toil/test/lib/test_history.py +4 -4
  105. toil/test/lib/test_trs.py +16 -14
  106. toil/test/lib/test_url.py +7 -6
  107. toil/test/lib/url_plugin_test.py +12 -18
  108. toil/test/provisioners/aws/awsProvisionerTest.py +10 -8
  109. toil/test/provisioners/clusterScalerTest.py +2 -5
  110. toil/test/provisioners/clusterTest.py +1 -3
  111. toil/test/server/serverTest.py +13 -4
  112. toil/test/sort/restart_sort.py +2 -6
  113. toil/test/sort/sort.py +3 -8
  114. toil/test/src/deferredFunctionTest.py +7 -7
  115. toil/test/src/environmentTest.py +1 -2
  116. toil/test/src/fileStoreTest.py +5 -5
  117. toil/test/src/importExportFileTest.py +5 -6
  118. toil/test/src/jobServiceTest.py +22 -14
  119. toil/test/src/jobTest.py +121 -25
  120. toil/test/src/miscTests.py +5 -7
  121. toil/test/src/promisedRequirementTest.py +8 -7
  122. toil/test/src/regularLogTest.py +2 -3
  123. toil/test/src/resourceTest.py +5 -8
  124. toil/test/src/restartDAGTest.py +5 -6
  125. toil/test/src/resumabilityTest.py +2 -2
  126. toil/test/src/retainTempDirTest.py +3 -3
  127. toil/test/src/systemTest.py +3 -3
  128. toil/test/src/threadingTest.py +1 -1
  129. toil/test/src/workerTest.py +1 -2
  130. toil/test/utils/toilDebugTest.py +6 -4
  131. toil/test/utils/toilKillTest.py +1 -1
  132. toil/test/utils/utilsTest.py +15 -14
  133. toil/test/wdl/wdltoil_test.py +247 -124
  134. toil/test/wdl/wdltoil_test_kubernetes.py +2 -2
  135. toil/toilState.py +2 -3
  136. toil/utils/toilDebugFile.py +3 -8
  137. toil/utils/toilDebugJob.py +1 -2
  138. toil/utils/toilLaunchCluster.py +1 -2
  139. toil/utils/toilSshCluster.py +2 -0
  140. toil/utils/toilStats.py +19 -24
  141. toil/utils/toilStatus.py +11 -14
  142. toil/version.py +10 -10
  143. toil/wdl/wdltoil.py +313 -209
  144. toil/worker.py +18 -12
  145. {toil-9.1.1.dist-info → toil-9.2.0.dist-info}/METADATA +11 -14
  146. {toil-9.1.1.dist-info → toil-9.2.0.dist-info}/RECORD +150 -153
  147. {toil-9.1.1.dist-info → toil-9.2.0.dist-info}/WHEEL +1 -1
  148. toil/test/cwl/staging_cat.cwl +0 -27
  149. toil/test/cwl/staging_make_file.cwl +0 -25
  150. toil/test/cwl/staging_workflow.cwl +0 -43
  151. toil/test/cwl/zero_default.cwl +0 -61
  152. toil/test/utils/ABCWorkflowDebug/ABC.txt +0 -1
  153. {toil-9.1.1.dist-info → toil-9.2.0.dist-info}/entry_points.txt +0 -0
  154. {toil-9.1.1.dist-info → toil-9.2.0.dist-info}/licenses/LICENSE +0 -0
  155. {toil-9.1.1.dist-info → toil-9.2.0.dist-info}/top_level.txt +0 -0
toil/cwl/utils.py CHANGED
@@ -18,27 +18,17 @@ import logging
18
18
  import os
19
19
  import posixpath
20
20
  import stat
21
- from collections.abc import Iterable, MutableMapping, MutableSequence
21
+ from collections.abc import Callable, Iterable, MutableMapping, MutableSequence
22
22
  from pathlib import PurePosixPath
23
- from typing import (
24
- Any,
25
- Callable,
26
- TypeVar,
27
- Union,
28
- Optional,
29
- cast,
30
- MutableSequence,
31
- MutableMapping,
32
- TYPE_CHECKING,
33
- )
23
+ from typing import TYPE_CHECKING, Any, TypeVar, Union, cast
34
24
  from urllib.parse import unquote, urlparse
35
25
 
36
26
  if TYPE_CHECKING:
37
27
  # This module needs to be importable even if cwltool is not installed.
38
28
  from cwltool.utils import CWLObjectType, CWLOutputType
29
+
39
30
  from toil.fileStores import FileID
40
31
  from toil.fileStores.abstractFileStore import AbstractFileStore
41
- from toil.jobStores.abstractJobStore import AbstractJobStore
42
32
  from toil.lib.url import URLAccess
43
33
 
44
34
  logger = logging.getLogger(__name__)
@@ -59,9 +49,9 @@ class CWLUnsupportedException(Exception):
59
49
  try:
60
50
  import cwltool.errors
61
51
 
62
- CWL_UNSUPPORTED_REQUIREMENT_EXCEPTION: Union[
63
- type[cwltool.errors.UnsupportedRequirement], type[CWLUnsupportedException]
64
- ] = cwltool.errors.UnsupportedRequirement
52
+ CWL_UNSUPPORTED_REQUIREMENT_EXCEPTION: (
53
+ type[cwltool.errors.UnsupportedRequirement] | type[CWLUnsupportedException]
54
+ ) = cwltool.errors.UnsupportedRequirement
65
55
  except ImportError:
66
56
  CWL_UNSUPPORTED_REQUIREMENT_EXCEPTION = CWLUnsupportedException
67
57
 
@@ -140,7 +130,7 @@ DirectoryStructure = dict[str, Union[str, "DirectoryStructure"]]
140
130
 
141
131
  def get_from_structure(
142
132
  dir_dict: DirectoryStructure, path: str
143
- ) -> Union[str, DirectoryStructure, None]:
133
+ ) -> str | DirectoryStructure | None:
144
134
  """
145
135
  Given a relative path, follow it in the given directory structure.
146
136
 
@@ -154,7 +144,7 @@ def get_from_structure(
154
144
  return dir_dict
155
145
  if parts[0] in ("..", "/"):
156
146
  raise RuntimeError(f"Path {path} not resolvable in virtual directory")
157
- found: Union[str, DirectoryStructure] = dir_dict
147
+ found: str | DirectoryStructure = dir_dict
158
148
  for part in parts:
159
149
  # Go down by each path component in turn
160
150
  if isinstance(found, str):
@@ -223,9 +213,7 @@ def download_structure(
223
213
  )
224
214
  else:
225
215
  # We need to download from some other kind of URL.
226
- size, executable = URLAccess.read_from_url(
227
- value, open(dest_path, "wb")
228
- )
216
+ size, executable = URLAccess.read_from_url(value, open(dest_path, "wb"))
229
217
  if executable:
230
218
  # Make the written file executable
231
219
  os.chmod(dest_path, os.stat(dest_path).st_mode | stat.S_IXUSR)
@@ -243,27 +231,34 @@ def trim_mounts_op_down(file_or_directory: "CWLObjectType") -> None:
243
231
  return
244
232
 
245
233
 
246
- def sniff_location(file_or_directory: "CWLObjectType") -> Optional[str]:
234
+ def sniff_location(file_or_directory: "CWLObjectType") -> str | None:
247
235
  """
248
236
  Get the local bare path for a CWL file or directory, or None.
249
237
 
250
238
  :return: None if we don't have a local path or file URI
251
239
  """
252
- if file_or_directory.get('location') is None and file_or_directory.get('path') is None:
240
+ if (
241
+ file_or_directory.get("location") is None
242
+ and file_or_directory.get("path") is None
243
+ ):
253
244
  # file or directory is defined by contents or listing respectively, this is not redundant
254
245
  return None
255
246
  # Since we only consider mountable paths, if path is not file URI or bare path, don't consider it
256
- path_or_url = cast(str, file_or_directory.get('location') or file_or_directory.get('path'))
247
+ path_or_url = cast(
248
+ str, file_or_directory.get("location") or file_or_directory.get("path")
249
+ )
257
250
  parsed = urlparse(path_or_url)
258
- if parsed.scheme == 'file':
251
+ if parsed.scheme == "file":
259
252
  return unquote(parsed.path)
260
- elif parsed.scheme == '':
253
+ elif parsed.scheme == "":
261
254
  return path_or_url
262
255
  else:
263
256
  return None
264
257
 
265
258
 
266
- def trim_mounts_op_up(file_or_directory: "CWLObjectType", op_down_ret: None, child_results: list[bool]) -> bool:
259
+ def trim_mounts_op_up(
260
+ file_or_directory: "CWLObjectType", op_down_ret: None, child_results: list[bool]
261
+ ) -> bool:
267
262
  """
268
263
  Remove subtrees of the CWL file or directory object tree that only have redundant stuff in them.
269
264
 
@@ -282,40 +277,54 @@ def trim_mounts_op_up(file_or_directory: "CWLObjectType", op_down_ret: None, chi
282
277
  if own_path is None:
283
278
  return True
284
279
  # basename should be set as we are the implementation
285
- own_basename = cast(str, file_or_directory['basename'])
280
+ own_basename = cast(str, file_or_directory["basename"])
286
281
 
287
282
  # If the basename does not match the path, then this is nonredundant
288
283
  if not own_path.endswith("/" + own_basename):
289
284
  return True
290
285
 
291
- if file_or_directory['class'] == 'File':
286
+ if file_or_directory["class"] == "File":
292
287
  if any(child_results):
293
288
  # one of the children was detected as not redundant
294
289
  return True
295
- for secondary in cast(MutableSequence[MutableMapping[str, "CWLOutputType"]], file_or_directory.get('secondaryFiles', [])):
290
+ for secondary in cast(
291
+ MutableSequence[MutableMapping[str, "CWLOutputType"]],
292
+ file_or_directory.get("secondaryFiles", []),
293
+ ):
296
294
  # secondary files should already be flagged nonredundant if they don't have either a path or location
297
295
  secondary_path = sniff_location(secondary)
298
- secondary_basename = cast(str, secondary['basename'])
296
+ secondary_basename = cast(str, secondary["basename"])
299
297
  # If we swap the secondary basename for the primary basename in the primary path, and they don't match, then they are nonredundant
300
- if os.path.join(own_path[:-len(own_basename)], secondary_basename) != secondary_path:
298
+ if (
299
+ os.path.join(own_path[: -len(own_basename)], secondary_basename)
300
+ != secondary_path
301
+ ):
301
302
  return True
302
303
  else:
303
- listings = cast(MutableSequence[MutableMapping[str, "CWLOutputType"]], file_or_directory.get('listing', []))
304
+ listings = cast(
305
+ MutableSequence[MutableMapping[str, "CWLOutputType"]],
306
+ file_or_directory.get("listing", []),
307
+ )
304
308
  if len(listings) == 0:
305
309
  return False
306
310
  # We assume child_results is in the same order as the directory listing
307
311
  # iterate backwards to avoid iteration issues
308
312
  for i in range(len(listings) - 1, -1, -1):
309
313
  if child_results[i] is False:
310
- if os.path.join(own_path, cast(str, listings[i]['basename'])) == sniff_location(listings[i]):
314
+ if os.path.join(
315
+ own_path, cast(str, listings[i]["basename"])
316
+ ) == sniff_location(listings[i]):
311
317
  del listings[i]
312
318
  # If one of the listings was nonredundant, then this directory is also nonredundant
313
319
  if any(child_results):
314
320
  return True
315
321
  return False
316
322
 
323
+
317
324
  def remove_redundant_mounts(cwljob: "CWLObjectType") -> None:
318
325
  """
319
326
  Remove any redundant mount points from the listing. Modifies the CWL object in place.
320
327
  """
321
- visit_cwl_class_and_reduce(cwljob, ["Directory", "File"], trim_mounts_op_down, trim_mounts_op_up)
328
+ visit_cwl_class_and_reduce(
329
+ cwljob, ["Directory", "File"], trim_mounts_op_down, trim_mounts_op_up
330
+ )
@@ -28,7 +28,9 @@ class FileID(str):
28
28
  the job store if unavailable in the ID.
29
29
  """
30
30
 
31
- def __new__(cls, fileStoreID: str, *args: Any, **kwargs: dict[str, Any]) -> "FileID":
31
+ def __new__(
32
+ cls, fileStoreID: str, *args: Any, **kwargs: dict[str, Any]
33
+ ) -> "FileID":
32
34
  return super().__new__(cls, fileStoreID)
33
35
 
34
36
  def __init__(self, fileStoreID: str, size: int, executable: bool = False) -> None:
@@ -14,7 +14,7 @@
14
14
  import logging
15
15
  import os
16
16
  from abc import ABC, abstractmethod
17
- from collections.abc import Generator, Iterator
17
+ from collections.abc import Callable, Generator, Iterator
18
18
  from contextlib import contextmanager
19
19
  from tempfile import mkstemp
20
20
  from threading import Event, Semaphore
@@ -22,10 +22,8 @@ from typing import (
22
22
  IO,
23
23
  TYPE_CHECKING,
24
24
  Any,
25
- Callable,
26
25
  ContextManager,
27
26
  Literal,
28
- Optional,
29
27
  Union,
30
28
  cast,
31
29
  overload,
@@ -118,7 +116,7 @@ class AbstractFileStore(ABC):
118
116
  )
119
117
  self.jobName: str = str(self.jobDesc)
120
118
  self.waitForPreviousCommit = waitForPreviousCommit
121
- self.logging_messages: list[dict[str, Union[int, str]]] = []
119
+ self.logging_messages: list[dict[str, int | str]] = []
122
120
  self.logging_user_streams: list[dict[str, str]] = []
123
121
  # Records file IDs of files deleted during the current job. Doesn't get
124
122
  # committed back until the job is completely successful, because if the
@@ -129,7 +127,7 @@ class AbstractFileStore(ABC):
129
127
  # the accessed files of failed jobs.
130
128
  self._accessLog: list[tuple[str, ...]] = []
131
129
  # Holds total bytes of observed disk usage for the last job run under open()
132
- self._job_disk_used: Optional[int] = None
130
+ self._job_disk_used: int | None = None
133
131
 
134
132
  @staticmethod
135
133
  def createFileStore(
@@ -137,14 +135,14 @@ class AbstractFileStore(ABC):
137
135
  jobDesc: JobDescription,
138
136
  file_store_dir: str,
139
137
  waitForPreviousCommit: Callable[[], Any],
140
- caching: Optional[bool],
138
+ caching: bool | None,
141
139
  ) -> Union["NonCachingFileStore", "CachingFileStore"]:
142
140
  """Create a concreate FileStore."""
143
141
  # Defer these imports until runtime, since these classes depend on us
144
142
  from toil.fileStores.cachingFileStore import CachingFileStore
145
143
  from toil.fileStores.nonCachingFileStore import NonCachingFileStore
146
144
 
147
- fileStoreCls: Union[type["CachingFileStore"], type["NonCachingFileStore"]] = (
145
+ fileStoreCls: type["CachingFileStore"] | type["NonCachingFileStore"] = (
148
146
  CachingFileStore if caching else NonCachingFileStore
149
147
  )
150
148
  return fileStoreCls(jobStore, jobDesc, file_store_dir, waitForPreviousCommit)
@@ -152,8 +150,8 @@ class AbstractFileStore(ABC):
152
150
  @staticmethod
153
151
  def shutdownFileStore(
154
152
  workflowID: str,
155
- config_work_dir: Optional[str],
156
- config_coordination_dir: Optional[str],
153
+ config_work_dir: str | None,
154
+ config_coordination_dir: str | None,
157
155
  ) -> None:
158
156
  """
159
157
  Carry out any necessary filestore-specific cleanup.
@@ -230,7 +228,7 @@ class AbstractFileStore(ABC):
230
228
  else:
231
229
  self.log_to_leader(disk_usage, level=logging.DEBUG)
232
230
 
233
- def get_disk_usage(self) -> Optional[int]:
231
+ def get_disk_usage(self) -> int | None:
234
232
  """
235
233
  Get the number of bytes of disk used by the last job run under open().
236
234
 
@@ -254,7 +252,7 @@ class AbstractFileStore(ABC):
254
252
  return os.path.abspath(mkdtemp(dir=self.localTempDir))
255
253
 
256
254
  def getLocalTempFile(
257
- self, suffix: Optional[str] = None, prefix: Optional[str] = None
255
+ self, suffix: str | None = None, prefix: str | None = None
258
256
  ) -> str:
259
257
  """
260
258
  Get a new local temporary file that will persist for the duration of the job.
@@ -278,7 +276,7 @@ class AbstractFileStore(ABC):
278
276
  return os.path.abspath(tmpFile)
279
277
 
280
278
  def getLocalTempFileName(
281
- self, suffix: Optional[str] = None, prefix: Optional[str] = None
279
+ self, suffix: str | None = None, prefix: str | None = None
282
280
  ) -> str:
283
281
  """
284
282
  Get a valid name for a new local file. Don't actually create a file at the path.
@@ -330,9 +328,9 @@ class AbstractFileStore(ABC):
330
328
  def writeGlobalFileStream(
331
329
  self,
332
330
  cleanup: bool = False,
333
- basename: Optional[str] = None,
334
- encoding: Optional[str] = None,
335
- errors: Optional[str] = None,
331
+ basename: str | None = None,
332
+ encoding: str | None = None,
333
+ errors: str | None = None,
336
334
  ) -> Iterator[tuple[WriteWatchingStream, FileID]]:
337
335
  """
338
336
  Similar to writeGlobalFile, but allows the writing of a stream to the job store.
@@ -423,7 +421,7 @@ class AbstractFileStore(ABC):
423
421
  logger.log(log_level, "Streamed file '%s'", *item)
424
422
 
425
423
  def logAccess(
426
- self, fileStoreID: Union[FileID, str], destination: Union[str, None] = None
424
+ self, fileStoreID: FileID | str, destination: str | None = None
427
425
  ) -> None:
428
426
  """
429
427
  Record that the given file was read by the job.
@@ -445,7 +443,7 @@ class AbstractFileStore(ABC):
445
443
  def readGlobalFile(
446
444
  self,
447
445
  fileStoreID: str,
448
- userPath: Optional[str] = None,
446
+ userPath: str | None = None,
449
447
  cache: bool = True,
450
448
  mutable: bool = False,
451
449
  symlink: bool = False,
@@ -486,21 +484,21 @@ class AbstractFileStore(ABC):
486
484
  self,
487
485
  fileStoreID: str,
488
486
  encoding: Literal[None] = None,
489
- errors: Optional[str] = None,
487
+ errors: str | None = None,
490
488
  ) -> ContextManager[IO[bytes]]: ...
491
489
 
492
490
  @overload
493
491
  def readGlobalFileStream(
494
- self, fileStoreID: str, encoding: str, errors: Optional[str] = None
492
+ self, fileStoreID: str, encoding: str, errors: str | None = None
495
493
  ) -> ContextManager[IO[str]]: ...
496
494
 
497
495
  @abstractmethod
498
496
  def readGlobalFileStream(
499
497
  self,
500
498
  fileStoreID: str,
501
- encoding: Optional[str] = None,
502
- errors: Optional[str] = None,
503
- ) -> ContextManager[Union[IO[bytes], IO[str]]]:
499
+ encoding: str | None = None,
500
+ errors: str | None = None,
501
+ ) -> ContextManager[IO[bytes] | IO[str]]:
504
502
  """
505
503
  Read a stream from the job store; similar to readGlobalFile.
506
504
 
@@ -519,7 +517,7 @@ class AbstractFileStore(ABC):
519
517
  """
520
518
  raise NotImplementedError()
521
519
 
522
- def getGlobalFileSize(self, fileStoreID: Union[FileID, str]) -> int:
520
+ def getGlobalFileSize(self, fileStoreID: FileID | str) -> int:
523
521
  """
524
522
  Get the size of the file pointed to by the given ID, in bytes.
525
523
 
@@ -547,7 +545,7 @@ class AbstractFileStore(ABC):
547
545
  return cast(int, size)
548
546
 
549
547
  @abstractmethod
550
- def deleteLocalFile(self, fileStoreID: Union[FileID, str]) -> None:
548
+ def deleteLocalFile(self, fileStoreID: FileID | str) -> None:
551
549
  """
552
550
  Delete local copies of files associated with the provided job store ID.
553
551
 
@@ -565,7 +563,7 @@ class AbstractFileStore(ABC):
565
563
  raise NotImplementedError()
566
564
 
567
565
  @abstractmethod
568
- def deleteGlobalFile(self, fileStoreID: Union[FileID, str]) -> None:
566
+ def deleteGlobalFile(self, fileStoreID: FileID | str) -> None:
569
567
  """
570
568
  Delete local files and then permanently deletes them from the job store.
571
569
 
@@ -580,13 +578,13 @@ class AbstractFileStore(ABC):
580
578
  # and the job store.
581
579
  @deprecated(new_function_name="import_file")
582
580
  def importFile(
583
- self, srcUrl: str, sharedFileName: Optional[str] = None
584
- ) -> Optional[FileID]:
581
+ self, srcUrl: str, sharedFileName: str | None = None
582
+ ) -> FileID | None:
585
583
  return self.import_file(srcUrl, sharedFileName)
586
584
 
587
585
  def import_file(
588
- self, src_uri: str, shared_file_name: Optional[str] = None
589
- ) -> Optional[FileID]:
586
+ self, src_uri: str, shared_file_name: str | None = None
587
+ ) -> FileID | None:
590
588
  return self.jobStore.import_file(src_uri, shared_file_name=shared_file_name)
591
589
 
592
590
  @deprecated(new_function_name="export_file")
@@ -625,7 +623,7 @@ class AbstractFileStore(ABC):
625
623
  @classmethod
626
624
  @abstractmethod
627
625
  @contextmanager
628
- def open(cls, outer: Optional[Any] = None) -> Iterator[Any]:
626
+ def open(cls, outer: Any | None = None) -> Iterator[Any]:
629
627
  """
630
628
  This is a context manager that state file and reads it into an object that is returned
631
629
  to the user in the yield.
@@ -22,10 +22,10 @@ import sqlite3
22
22
  import stat
23
23
  import threading
24
24
  import time
25
- from collections.abc import Generator, Iterator, Sequence
25
+ from collections.abc import Callable, Generator, Iterator, Sequence
26
26
  from contextlib import contextmanager
27
27
  from tempfile import mkstemp
28
- from typing import Any, Callable, Optional
28
+ from typing import Any
29
29
 
30
30
  from toil.common import cacheDirName, getFileSystemSize
31
31
  from toil.fileStores import FileID
@@ -221,7 +221,7 @@ class CachingFileStore(AbstractFileStore):
221
221
  logger.debug("Starting job (%s) with ID (%s).", self.jobName, self.jobID)
222
222
 
223
223
  # When the job actually starts, we will fill this in with the job's disk requirement.
224
- self.jobDiskBytes: Optional[float] = None
224
+ self.jobDiskBytes: float | None = None
225
225
 
226
226
  # We need to track what attempt of the workflow we are, to prevent crosstalk between attempts' caches.
227
227
  self.workflowAttemptNumber = self.jobStore.config.workflowAttemptNumber
@@ -386,7 +386,7 @@ class CachingFileStore(AbstractFileStore):
386
386
  ],
387
387
  )
388
388
  def _static_read(
389
- cur: sqlite3.Cursor, query: str, args: Optional[Sequence[Any]] = ()
389
+ cur: sqlite3.Cursor, query: str, args: Sequence[Any] | None = ()
390
390
  ) -> Iterator[Any]:
391
391
  """
392
392
  Read from the database.
@@ -398,7 +398,7 @@ class CachingFileStore(AbstractFileStore):
398
398
  # All the real work is the decorators
399
399
  return cur.execute(query, args)
400
400
 
401
- def _read(self, query: str, args: Optional[Sequence[Any]] = ()) -> Iterator[Any]:
401
+ def _read(self, query: str, args: Sequence[Any] | None = ()) -> Iterator[Any]:
402
402
  """
403
403
  Read from the database using the instance's connection.
404
404
 
@@ -2010,7 +2010,7 @@ class CachingFileStore(AbstractFileStore):
2010
2010
  self,
2011
2011
  file_store_id: FileID,
2012
2012
  reader_id: str,
2013
- local_file_path: Optional[str] = None,
2013
+ local_file_path: str | None = None,
2014
2014
  ) -> Generator:
2015
2015
  """
2016
2016
  Get a context manager that gives you either the local file path for a
@@ -2275,7 +2275,7 @@ class CachingFileStore(AbstractFileStore):
2275
2275
  if len(self.jobDesc.filesToDelete) > 0:
2276
2276
  raise RuntimeError("Job is already in the process of being committed!")
2277
2277
 
2278
- state_to_commit: Optional[JobDescription] = None
2278
+ state_to_commit: JobDescription | None = None
2279
2279
 
2280
2280
  if jobState:
2281
2281
  # Clone the current job description, so that further updates to it
@@ -2308,7 +2308,7 @@ class CachingFileStore(AbstractFileStore):
2308
2308
  )
2309
2309
  self.commitThread.start()
2310
2310
 
2311
- def startCommitThread(self, state_to_commit: Optional[JobDescription]):
2311
+ def startCommitThread(self, state_to_commit: JobDescription | None):
2312
2312
  """
2313
2313
  Run in a thread to actually commit the current job.
2314
2314
  """
@@ -16,20 +16,9 @@ import logging
16
16
  import os
17
17
  import tempfile
18
18
  from collections import defaultdict
19
- from collections.abc import Generator, Iterator
19
+ from collections.abc import Callable, Generator, Iterator
20
20
  from contextlib import contextmanager
21
- from typing import (
22
- IO,
23
- Any,
24
- Callable,
25
- ContextManager,
26
- DefaultDict,
27
- Literal,
28
- Optional,
29
- Union,
30
- cast,
31
- overload,
32
- )
21
+ from typing import IO, Any, ContextManager, DefaultDict, Literal, cast, overload
33
22
 
34
23
  import dill
35
24
 
@@ -61,13 +50,13 @@ class NonCachingFileStore(AbstractFileStore):
61
50
  ) -> None:
62
51
  super().__init__(jobStore, jobDesc, file_store_dir, waitForPreviousCommit)
63
52
  # This will be defined in the `open` method.
64
- self.jobStateFile: Optional[str] = None
53
+ self.jobStateFile: str | None = None
65
54
  self.localFileMap: DefaultDict[str, list[str]] = defaultdict(list)
66
55
 
67
56
  self.check_for_state_corruption()
68
57
 
69
58
  @staticmethod
70
- def check_for_coordination_corruption(coordination_dir: Optional[str]) -> None:
59
+ def check_for_coordination_corruption(coordination_dir: str | None) -> None:
71
60
  """
72
61
  Make sure the coordination directory hasn't been deleted unexpectedly.
73
62
 
@@ -145,7 +134,7 @@ class NonCachingFileStore(AbstractFileStore):
145
134
  def readGlobalFile(
146
135
  self,
147
136
  fileStoreID: str,
148
- userPath: Optional[str] = None,
137
+ userPath: str | None = None,
149
138
  cache: bool = True,
150
139
  mutable: bool = False,
151
140
  symlink: bool = False,
@@ -169,12 +158,12 @@ class NonCachingFileStore(AbstractFileStore):
169
158
  self,
170
159
  fileStoreID: str,
171
160
  encoding: Literal[None] = None,
172
- errors: Optional[str] = None,
161
+ errors: str | None = None,
173
162
  ) -> ContextManager[IO[bytes]]: ...
174
163
 
175
164
  @overload
176
165
  def readGlobalFileStream(
177
- self, fileStoreID: str, encoding: str, errors: Optional[str] = None
166
+ self, fileStoreID: str, encoding: str, errors: str | None = None
178
167
  ) -> ContextManager[IO[str]]: ...
179
168
 
180
169
  # TODO: This seems to hit https://github.com/python/mypy/issues/11373
@@ -184,9 +173,9 @@ class NonCachingFileStore(AbstractFileStore):
184
173
  def readGlobalFileStream(
185
174
  self,
186
175
  fileStoreID: str,
187
- encoding: Optional[str] = None,
188
- errors: Optional[str] = None,
189
- ) -> Iterator[Union[IO[bytes], IO[str]]]:
176
+ encoding: str | None = None,
177
+ errors: str | None = None,
178
+ ) -> Iterator[IO[bytes] | IO[str]]:
190
179
  with self.jobStore.read_file_stream(
191
180
  fileStoreID, encoding=encoding, errors=errors
192
181
  ) as f: