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
@@ -17,7 +17,7 @@ import pickle
17
17
  import re
18
18
  import shutil
19
19
  from abc import ABC, ABCMeta, abstractmethod
20
- from collections.abc import Iterator, ValuesView
20
+ from collections.abc import Callable, Iterator, ValuesView
21
21
  from contextlib import closing, contextmanager
22
22
  from datetime import timedelta
23
23
  from http.client import BadStatusLine
@@ -25,18 +25,15 @@ from typing import (
25
25
  IO,
26
26
  TYPE_CHECKING,
27
27
  Any,
28
- Callable,
29
28
  ContextManager,
30
29
  Literal,
31
- Optional,
32
30
  Union,
33
31
  cast,
34
32
  overload,
35
- Type,
36
33
  )
37
34
  from urllib.error import HTTPError
38
35
  from urllib.parse import ParseResult, urlparse
39
- from urllib.request import urlopen, Request
36
+ from urllib.request import Request, urlopen
40
37
  from uuid import uuid4
41
38
 
42
39
  from toil.common import Config, getNodeID, safeUnpickleFromStream
@@ -47,9 +44,8 @@ from toil.job import (
47
44
  JobException,
48
45
  ServiceJobDescription,
49
46
  )
50
- from toil.lib.ftp_utils import FtpFsAccess
51
47
  from toil.lib.compatibility import deprecated
52
- from toil.lib.exceptions import UnimplementedURLException
48
+ from toil.lib.ftp_utils import FtpFsAccess
53
49
  from toil.lib.io import WriteWatchingStream
54
50
  from toil.lib.memoize import memoize
55
51
  from toil.lib.retry import ErrorCondition, retry
@@ -74,7 +70,7 @@ class LocatorException(Exception):
74
70
  For example, job store/aws bucket exceptions where they already exist
75
71
  """
76
72
 
77
- def __init__(self, error_msg: str, locator: str, prefix: Optional[str] = None):
73
+ def __init__(self, error_msg: str, locator: str, prefix: str | None = None):
78
74
  full_locator = locator if prefix is None else f"{prefix}:{locator}"
79
75
  super().__init__(error_msg % full_locator)
80
76
 
@@ -90,7 +86,7 @@ class InvalidImportExportUrlException(Exception):
90
86
  class NoSuchJobException(Exception):
91
87
  """Indicates that the specified job does not exist."""
92
88
 
93
- def __init__(self, jobStoreID: Union[FileID, str]):
89
+ def __init__(self, jobStoreID: FileID | str):
94
90
  """
95
91
  :param str jobStoreID: the jobStoreID that was mistakenly assumed to exist
96
92
  """
@@ -100,7 +96,7 @@ class NoSuchJobException(Exception):
100
96
  class ConcurrentFileModificationException(Exception):
101
97
  """Indicates that the file was attempted to be modified by multiple processes at once."""
102
98
 
103
- def __init__(self, jobStoreFileID: Union[FileID, str]):
99
+ def __init__(self, jobStoreFileID: FileID | str):
104
100
  """
105
101
  :param jobStoreFileID: the ID of the file that was modified by multiple workers
106
102
  or processes concurrently
@@ -112,7 +108,10 @@ class NoSuchFileException(Exception):
112
108
  """Indicates that the specified file does not exist."""
113
109
 
114
110
  def __init__(
115
- self, jobStoreFileID: Union[FileID, str], customName: Optional[str] = None, *extra: Any
111
+ self,
112
+ jobStoreFileID: FileID | str,
113
+ customName: str | None = None,
114
+ *extra: Any,
116
115
  ):
117
116
  """
118
117
  :param jobStoreFileID: the ID of the file that was mistakenly assumed to exist
@@ -145,7 +144,7 @@ class NoSuchJobStoreException(LocatorException):
145
144
  super().__init__(
146
145
  "The job store '%s' does not exist, so there is nothing to restart.",
147
146
  locator,
148
- prefix
147
+ prefix,
149
148
  )
150
149
 
151
150
 
@@ -160,7 +159,7 @@ class JobStoreExistsException(LocatorException):
160
159
  "The job store '%s' already exists. Use --restart to resume the workflow, or remove "
161
160
  "the job store with 'toil clean' to start the workflow from scratch.",
162
161
  locator,
163
- prefix
162
+ prefix,
164
163
  )
165
164
 
166
165
 
@@ -362,7 +361,6 @@ class AbstractJobStore(ABC):
362
361
  jobStoreClasses.append(jobStoreClass)
363
362
  return jobStoreClasses
364
363
 
365
-
366
364
  # Importing a file with a shared file name returns None, but without one it
367
365
  # returns a file ID. Explain this to MyPy.
368
366
 
@@ -388,10 +386,10 @@ class AbstractJobStore(ABC):
388
386
  def importFile(
389
387
  self,
390
388
  srcUrl: str,
391
- sharedFileName: Optional[str] = None,
389
+ sharedFileName: str | None = None,
392
390
  hardlink: bool = False,
393
391
  symlink: bool = True,
394
- ) -> Optional[FileID]:
392
+ ) -> FileID | None:
395
393
  return self.import_file(srcUrl, sharedFileName, hardlink, symlink)
396
394
 
397
395
  @overload
@@ -415,10 +413,10 @@ class AbstractJobStore(ABC):
415
413
  def import_file(
416
414
  self,
417
415
  src_uri: str,
418
- shared_file_name: Optional[str] = None,
416
+ shared_file_name: str | None = None,
419
417
  hardlink: bool = False,
420
418
  symlink: bool = True,
421
- ) -> Optional[FileID]:
419
+ ) -> FileID | None:
422
420
  """
423
421
  Imports the file at the given URL into job store. The ID of the newly imported file is
424
422
  returned. If the name of a shared file name is provided, the file will be imported as
@@ -467,12 +465,12 @@ class AbstractJobStore(ABC):
467
465
 
468
466
  def _import_file(
469
467
  self,
470
- otherCls: Type["URLAccess"],
468
+ otherCls: type["URLAccess"],
471
469
  uri: ParseResult,
472
- shared_file_name: Optional[str] = None,
470
+ shared_file_name: str | None = None,
473
471
  hardlink: bool = False,
474
472
  symlink: bool = True,
475
- ) -> Optional[FileID]:
473
+ ) -> FileID | None:
476
474
  """
477
475
  Import the file at the given URL using the given job store class to retrieve that file.
478
476
  See also :meth:`.importFile`. This method applies a generic approach to importing: it
@@ -524,13 +522,14 @@ class AbstractJobStore(ABC):
524
522
  supported URL scheme e.g. a blob in an AWS s3 bucket. May also be a local path.
525
523
  """
526
524
  from toil.common import Toil
525
+
527
526
  dst_uri = Toil.normalize_uri(dst_uri)
528
527
  parseResult = urlparse(dst_uri)
529
528
  otherCls = URLAccess._find_url_implementation(parseResult, export=True)
530
529
  self._export_file(otherCls, file_id, parseResult)
531
530
 
532
531
  def _export_file(
533
- self, otherCls: Type["URLAccess"], jobStoreFileID: FileID, url: ParseResult
532
+ self, otherCls: type["URLAccess"], jobStoreFileID: FileID, url: ParseResult
534
533
  ) -> None:
535
534
  """
536
535
  Refer to exportFile docstring for information about this method.
@@ -547,7 +546,7 @@ class AbstractJobStore(ABC):
547
546
  self._default_export_file(otherCls, jobStoreFileID, url)
548
547
 
549
548
  def _default_export_file(
550
- self, otherCls: Type["URLAccess"], jobStoreFileID: FileID, url: ParseResult
549
+ self, otherCls: type["URLAccess"], jobStoreFileID: FileID, url: ParseResult
551
550
  ) -> None:
552
551
  """
553
552
  Refer to exportFile docstring for information about this method.
@@ -597,7 +596,7 @@ class AbstractJobStore(ABC):
597
596
 
598
597
  # Cleanup functions
599
598
  def clean(
600
- self, jobCache: Optional[dict[Union[str, "TemporaryID"], JobDescription]] = None
599
+ self, jobCache: dict[Union[str, "TemporaryID"], JobDescription] | None = None
601
600
  ) -> JobDescription:
602
601
  """
603
602
  Function to cleanup the state of a job store after a restart.
@@ -648,7 +647,7 @@ class AbstractJobStore(ABC):
648
647
  self.update_job(jobDescription)
649
648
 
650
649
  def getJobDescriptions() -> (
651
- Union[ValuesView[JobDescription], Iterator[JobDescription]]
650
+ ValuesView[JobDescription] | Iterator[JobDescription]
652
651
  ):
653
652
  if jobCache is not None:
654
653
  return jobCache.values()
@@ -904,7 +903,7 @@ class AbstractJobStore(ABC):
904
903
 
905
904
  # We have to manually discard the stream to avoid getting
906
905
  # stuck on a blocking write from the job store.
907
- def discardStream(stream: Union[IO[bytes], IO[str]]) -> None:
906
+ def discardStream(stream: IO[bytes] | IO[str]) -> None:
908
907
  """Read the stream 4K at a time until EOF, discarding all input."""
909
908
  while len(stream.read(4096)) != 0:
910
909
  pass
@@ -1093,14 +1092,14 @@ class AbstractJobStore(ABC):
1093
1092
  def writeFile(
1094
1093
  self,
1095
1094
  localFilePath: str,
1096
- jobStoreID: Optional[str] = None,
1095
+ jobStoreID: str | None = None,
1097
1096
  cleanup: bool = False,
1098
1097
  ) -> str:
1099
1098
  return self.write_file(localFilePath, jobStoreID, cleanup)
1100
1099
 
1101
1100
  @abstractmethod
1102
1101
  def write_file(
1103
- self, local_path: str, job_id: Optional[str] = None, cleanup: bool = False
1102
+ self, local_path: str, job_id: str | None = None, cleanup: bool = False
1104
1103
  ) -> str:
1105
1104
  """
1106
1105
  Takes a file (as a path) and places it in this job store. Returns an ID that can be used
@@ -1135,11 +1134,11 @@ class AbstractJobStore(ABC):
1135
1134
  @deprecated(new_function_name="write_file_stream")
1136
1135
  def writeFileStream(
1137
1136
  self,
1138
- jobStoreID: Optional[str] = None,
1137
+ jobStoreID: str | None = None,
1139
1138
  cleanup: bool = False,
1140
- basename: Optional[str] = None,
1141
- encoding: Optional[str] = None,
1142
- errors: Optional[str] = None,
1139
+ basename: str | None = None,
1140
+ encoding: str | None = None,
1141
+ errors: str | None = None,
1143
1142
  ) -> ContextManager[tuple[IO[bytes], str]]:
1144
1143
  return self.write_file_stream(jobStoreID, cleanup, basename, encoding, errors)
1145
1144
 
@@ -1147,11 +1146,11 @@ class AbstractJobStore(ABC):
1147
1146
  @contextmanager
1148
1147
  def write_file_stream(
1149
1148
  self,
1150
- job_id: Optional[str] = None,
1149
+ job_id: str | None = None,
1151
1150
  cleanup: bool = False,
1152
- basename: Optional[str] = None,
1153
- encoding: Optional[str] = None,
1154
- errors: Optional[str] = None,
1151
+ basename: str | None = None,
1152
+ encoding: str | None = None,
1153
+ errors: str | None = None,
1155
1154
  ) -> Iterator[tuple[IO[bytes], str]]:
1156
1155
  """
1157
1156
  Similar to writeFile, but returns a context manager yielding a tuple of
@@ -1194,18 +1193,18 @@ class AbstractJobStore(ABC):
1194
1193
  @deprecated(new_function_name="get_empty_file_store_id")
1195
1194
  def getEmptyFileStoreID(
1196
1195
  self,
1197
- jobStoreID: Optional[str] = None,
1196
+ jobStoreID: str | None = None,
1198
1197
  cleanup: bool = False,
1199
- basename: Optional[str] = None,
1198
+ basename: str | None = None,
1200
1199
  ) -> str:
1201
1200
  return self.get_empty_file_store_id(jobStoreID, cleanup, basename)
1202
1201
 
1203
1202
  @abstractmethod
1204
1203
  def get_empty_file_store_id(
1205
1204
  self,
1206
- job_id: Optional[str] = None,
1205
+ job_id: str | None = None,
1207
1206
  cleanup: bool = False,
1208
- basename: Optional[str] = None,
1207
+ basename: str | None = None,
1209
1208
  ) -> str:
1210
1209
  """
1211
1210
  Creates an empty file in the job store and returns its ID.
@@ -1263,31 +1262,31 @@ class AbstractJobStore(ABC):
1263
1262
  def readFileStream(
1264
1263
  self,
1265
1264
  jobStoreFileID: str,
1266
- encoding: Optional[str] = None,
1267
- errors: Optional[str] = None,
1268
- ) -> Union[ContextManager[IO[bytes]], ContextManager[IO[str]]]:
1265
+ encoding: str | None = None,
1266
+ errors: str | None = None,
1267
+ ) -> ContextManager[IO[bytes]] | ContextManager[IO[str]]:
1269
1268
  return self.read_file_stream(jobStoreFileID, encoding, errors)
1270
1269
 
1271
1270
  @overload
1272
1271
  def read_file_stream(
1273
1272
  self,
1274
- file_id: Union[FileID, str],
1273
+ file_id: FileID | str,
1275
1274
  encoding: Literal[None] = None,
1276
- errors: Optional[str] = None,
1275
+ errors: str | None = None,
1277
1276
  ) -> ContextManager[IO[bytes]]: ...
1278
1277
 
1279
1278
  @overload
1280
1279
  def read_file_stream(
1281
- self, file_id: Union[FileID, str], encoding: str, errors: Optional[str] = None
1280
+ self, file_id: FileID | str, encoding: str, errors: str | None = None
1282
1281
  ) -> ContextManager[IO[str]]: ...
1283
1282
 
1284
1283
  @abstractmethod
1285
1284
  def read_file_stream(
1286
1285
  self,
1287
- file_id: Union[FileID, str],
1288
- encoding: Optional[str] = None,
1289
- errors: Optional[str] = None,
1290
- ) -> Union[ContextManager[IO[bytes]], ContextManager[IO[str]]]:
1286
+ file_id: FileID | str,
1287
+ encoding: str | None = None,
1288
+ errors: str | None = None,
1289
+ ) -> ContextManager[IO[bytes]] | ContextManager[IO[str]]:
1291
1290
  """
1292
1291
  Similar to readFile, but returns a context manager yielding a file handle which can be
1293
1292
  read from. The yielded file handle does not need to and should not be closed explicitly.
@@ -1378,15 +1377,15 @@ class AbstractJobStore(ABC):
1378
1377
  def updateFileStream(
1379
1378
  self,
1380
1379
  jobStoreFileID: str,
1381
- encoding: Optional[str] = None,
1382
- errors: Optional[str] = None,
1380
+ encoding: str | None = None,
1381
+ errors: str | None = None,
1383
1382
  ) -> ContextManager[IO[Any]]:
1384
1383
  return self.update_file_stream(jobStoreFileID, encoding, errors)
1385
1384
 
1386
1385
  @abstractmethod
1387
1386
  @contextmanager
1388
1387
  def update_file_stream(
1389
- self, file_id: str, encoding: Optional[str] = None, errors: Optional[str] = None
1388
+ self, file_id: str, encoding: str | None = None, errors: str | None = None
1390
1389
  ) -> Iterator[IO[Any]]:
1391
1390
  """
1392
1391
  Replaces the existing version of a file in the job store. Similar to writeFile, but
@@ -1419,9 +1418,9 @@ class AbstractJobStore(ABC):
1419
1418
  def writeSharedFileStream(
1420
1419
  self,
1421
1420
  sharedFileName: str,
1422
- isProtected: Optional[bool] = None,
1423
- encoding: Optional[str] = None,
1424
- errors: Optional[str] = None,
1421
+ isProtected: bool | None = None,
1422
+ encoding: str | None = None,
1423
+ errors: str | None = None,
1425
1424
  ) -> ContextManager[IO[bytes]]:
1426
1425
  return self.write_shared_file_stream(
1427
1426
  sharedFileName, isProtected, encoding, errors
@@ -1432,9 +1431,9 @@ class AbstractJobStore(ABC):
1432
1431
  def write_shared_file_stream(
1433
1432
  self,
1434
1433
  shared_file_name: str,
1435
- encrypted: Optional[bool] = None,
1436
- encoding: Optional[str] = None,
1437
- errors: Optional[str] = None,
1434
+ encrypted: bool | None = None,
1435
+ encoding: str | None = None,
1436
+ errors: str | None = None,
1438
1437
  ) -> Iterator[IO[bytes]]:
1439
1438
  """
1440
1439
  Returns a context manager yielding a writable file handle to the global file referenced
@@ -1464,9 +1463,9 @@ class AbstractJobStore(ABC):
1464
1463
  def readSharedFileStream(
1465
1464
  self,
1466
1465
  sharedFileName: str,
1467
- encoding: Optional[str] = None,
1468
- errors: Optional[str] = None,
1469
- ) -> Union[ContextManager[IO[str]], ContextManager[IO[bytes]]]:
1466
+ encoding: str | None = None,
1467
+ errors: str | None = None,
1468
+ ) -> ContextManager[IO[str]] | ContextManager[IO[bytes]]:
1470
1469
  return self.read_shared_file_stream(sharedFileName, encoding, errors)
1471
1470
 
1472
1471
  @overload
@@ -1476,7 +1475,7 @@ class AbstractJobStore(ABC):
1476
1475
  self,
1477
1476
  shared_file_name: str,
1478
1477
  encoding: str,
1479
- errors: Optional[str] = None,
1478
+ errors: str | None = None,
1480
1479
  ) -> Iterator[IO[str]]:
1481
1480
  """If encoding is specified, then a text file handle is provided."""
1482
1481
 
@@ -1487,7 +1486,7 @@ class AbstractJobStore(ABC):
1487
1486
  self,
1488
1487
  shared_file_name: str,
1489
1488
  encoding: Literal[None] = None,
1490
- errors: Optional[str] = None,
1489
+ errors: str | None = None,
1491
1490
  ) -> Iterator[IO[bytes]]:
1492
1491
  """If no encoding is provided, then a bytest file handle is provided."""
1493
1492
 
@@ -1496,9 +1495,9 @@ class AbstractJobStore(ABC):
1496
1495
  def read_shared_file_stream(
1497
1496
  self,
1498
1497
  shared_file_name: str,
1499
- encoding: Optional[str] = None,
1500
- errors: Optional[str] = None,
1501
- ) -> Union[Iterator[IO[str]], Iterator[IO[bytes]]]:
1498
+ encoding: str | None = None,
1499
+ errors: str | None = None,
1500
+ ) -> Iterator[IO[str]] | Iterator[IO[bytes]]:
1502
1501
  """
1503
1502
  Returns a context manager yielding a readable file handle to the global file referenced
1504
1503
  by the given name.
@@ -1693,7 +1692,7 @@ class JobStoreSupport(AbstractJobStore, URLAccess, metaclass=ABCMeta):
1693
1692
  ErrorCondition(error=HTTPError, error_codes=[408, 500, 503]),
1694
1693
  ]
1695
1694
  )
1696
- def _get_size(cls, url: ParseResult) -> Optional[int]:
1695
+ def _get_size(cls, url: ParseResult) -> int | None:
1697
1696
  if url.scheme.lower() == "ftp":
1698
1697
  ftp = cls._setup_ftp()
1699
1698
  return ftp.size(url.geturl())
@@ -1705,7 +1704,7 @@ class JobStoreSupport(AbstractJobStore, URLAccess, metaclass=ABCMeta):
1705
1704
 
1706
1705
  @classmethod
1707
1706
  def _read_from_url(
1708
- cls, url: ParseResult, writable: Union[IO[bytes], IO[str]]
1707
+ cls, url: ParseResult, writable: IO[bytes] | IO[str]
1709
1708
  ) -> tuple[int, bool]:
1710
1709
  # We can't actually retry after we start writing.
1711
1710
  # TODO: Implement retry with byte range requests