foamlib 0.6.10__tar.gz → 0.6.12__tar.gz

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 (28) hide show
  1. {foamlib-0.6.10 → foamlib-0.6.12}/PKG-INFO +1 -1
  2. {foamlib-0.6.10 → foamlib-0.6.12}/foamlib/__init__.py +1 -1
  3. {foamlib-0.6.10 → foamlib-0.6.12}/foamlib/_cases/_async.py +23 -23
  4. {foamlib-0.6.10 → foamlib-0.6.12}/foamlib/_cases/_base.py +25 -26
  5. {foamlib-0.6.10 → foamlib-0.6.12}/foamlib/_cases/_run.py +52 -58
  6. {foamlib-0.6.10 → foamlib-0.6.12}/foamlib/_cases/_slurm.py +7 -5
  7. {foamlib-0.6.10 → foamlib-0.6.12}/foamlib/_cases/_subprocess.py +15 -13
  8. {foamlib-0.6.10 → foamlib-0.6.12}/foamlib/_cases/_sync.py +25 -27
  9. {foamlib-0.6.10 → foamlib-0.6.12}/foamlib/_cases/_util.py +13 -10
  10. {foamlib-0.6.10 → foamlib-0.6.12}/foamlib/_files/_base.py +37 -27
  11. {foamlib-0.6.10 → foamlib-0.6.12}/foamlib/_files/_files.py +143 -159
  12. foamlib-0.6.12/foamlib/_files/_io.py +79 -0
  13. {foamlib-0.6.10 → foamlib-0.6.12}/foamlib/_files/_parsing.py +68 -25
  14. {foamlib-0.6.10 → foamlib-0.6.12}/foamlib/_files/_serialization.py +20 -12
  15. {foamlib-0.6.10 → foamlib-0.6.12}/foamlib/_files/_util.py +8 -3
  16. {foamlib-0.6.10 → foamlib-0.6.12}/foamlib.egg-info/PKG-INFO +1 -1
  17. {foamlib-0.6.10 → foamlib-0.6.12}/pyproject.toml +18 -29
  18. foamlib-0.6.10/foamlib/_files/_io.py +0 -90
  19. {foamlib-0.6.10 → foamlib-0.6.12}/LICENSE.txt +0 -0
  20. {foamlib-0.6.10 → foamlib-0.6.12}/README.md +0 -0
  21. {foamlib-0.6.10 → foamlib-0.6.12}/foamlib/_cases/__init__.py +0 -0
  22. {foamlib-0.6.10 → foamlib-0.6.12}/foamlib/_files/__init__.py +0 -0
  23. {foamlib-0.6.10 → foamlib-0.6.12}/foamlib/py.typed +0 -0
  24. {foamlib-0.6.10 → foamlib-0.6.12}/foamlib.egg-info/SOURCES.txt +0 -0
  25. {foamlib-0.6.10 → foamlib-0.6.12}/foamlib.egg-info/dependency_links.txt +0 -0
  26. {foamlib-0.6.10 → foamlib-0.6.12}/foamlib.egg-info/requires.txt +0 -0
  27. {foamlib-0.6.10 → foamlib-0.6.12}/foamlib.egg-info/top_level.txt +0 -0
  28. {foamlib-0.6.10 → foamlib-0.6.12}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: foamlib
3
- Version: 0.6.10
3
+ Version: 0.6.12
4
4
  Summary: A Python interface for interacting with OpenFOAM
5
5
  Author-email: "Gabriel S. Gerlero" <ggerlero@cimec.unl.edu.ar>
6
6
  Project-URL: Homepage, https://github.com/gerlero/foamlib
@@ -1,6 +1,6 @@
1
1
  """A Python interface for interacting with OpenFOAM."""
2
2
 
3
- __version__ = "0.6.10"
3
+ __version__ = "0.6.12"
4
4
 
5
5
  from ._cases import (
6
6
  AsyncFoamCase,
@@ -1,8 +1,10 @@
1
+ from __future__ import annotations
2
+
1
3
  import asyncio
2
4
  import multiprocessing
3
5
  import sys
4
6
  from contextlib import asynccontextmanager
5
- from typing import TYPE_CHECKING, Any, Callable, Optional, TypeVar, Union, overload
7
+ from typing import TYPE_CHECKING, Any, Callable, TypeVar, overload
6
8
 
7
9
  if sys.version_info >= (3, 9):
8
10
  from collections.abc import (
@@ -22,7 +24,6 @@ else:
22
24
 
23
25
  import aioshutil
24
26
 
25
- from .._files import FoamFieldFile
26
27
  from ._base import FoamCaseBase
27
28
  from ._run import FoamCaseRunBase
28
29
  from ._subprocess import run_async
@@ -31,6 +32,8 @@ from ._util import ValuedGenerator, awaitableasynccontextmanager
31
32
  if TYPE_CHECKING:
32
33
  import os
33
34
 
35
+ from .._files import FoamFieldFile
36
+
34
37
  X = TypeVar("X")
35
38
  Y = TypeVar("Y")
36
39
 
@@ -48,7 +51,7 @@ class AsyncFoamCase(FoamCaseRunBase):
48
51
 
49
52
  class TimeDirectory(FoamCaseRunBase.TimeDirectory):
50
53
  @property
51
- def _case(self) -> "AsyncFoamCase":
54
+ def _case(self) -> AsyncFoamCase:
52
55
  return AsyncFoamCase(self.path.parent)
53
56
 
54
57
  async def cell_centers(self) -> FoamFieldFile:
@@ -62,8 +65,8 @@ class AsyncFoamCase(FoamCaseRunBase):
62
65
 
63
66
  max_cpus = multiprocessing.cpu_count()
64
67
  """
65
- Maximum number of CPUs to use for running instances of `AsyncFoamCase` concurrently.
66
-
68
+ Maximum number of CPUs to use for running instances of `AsyncFoamCase` concurrently.
69
+
67
70
  Defaults to the number of CPUs on the system.
68
71
  """
69
72
 
@@ -91,7 +94,7 @@ class AsyncFoamCase(FoamCaseRunBase):
91
94
 
92
95
  @staticmethod
93
96
  async def _run(
94
- cmd: Union[Sequence[Union[str, "os.PathLike[str]"]], str],
97
+ cmd: Sequence[str | os.PathLike[str]] | str,
95
98
  *,
96
99
  cpus: int,
97
100
  **kwargs: Any,
@@ -101,19 +104,18 @@ class AsyncFoamCase(FoamCaseRunBase):
101
104
 
102
105
  @staticmethod
103
106
  async def _rmtree(
104
- path: Union["os.PathLike[str]", str], ignore_errors: bool = False
107
+ path: os.PathLike[str] | str, *, ignore_errors: bool = False
105
108
  ) -> None:
106
109
  await aioshutil.rmtree(path, ignore_errors=ignore_errors)
107
110
 
108
111
  @staticmethod
109
112
  async def _copytree(
110
- src: Union["os.PathLike[str]", str],
111
- dest: Union["os.PathLike[str]", str],
113
+ src: os.PathLike[str] | str,
114
+ dest: os.PathLike[str] | str,
112
115
  *,
113
116
  symlinks: bool = False,
114
- ignore: Optional[
115
- Callable[[Union["os.PathLike[str]", str], Collection[str]], Collection[str]]
116
- ] = None,
117
+ ignore: Callable[[os.PathLike[str] | str, Collection[str]], Collection[str]]
118
+ | None = None,
117
119
  ) -> None:
118
120
  await aioshutil.copytree(src, dest, symlinks=symlinks, ignore=ignore)
119
121
 
@@ -127,16 +129,14 @@ class AsyncFoamCase(FoamCaseRunBase):
127
129
  await coro
128
130
 
129
131
  @overload
130
- def __getitem__(
131
- self, index: Union[int, float, str]
132
- ) -> "AsyncFoamCase.TimeDirectory": ...
132
+ def __getitem__(self, index: int | float | str) -> AsyncFoamCase.TimeDirectory: ...
133
133
 
134
134
  @overload
135
- def __getitem__(self, index: slice) -> Sequence["AsyncFoamCase.TimeDirectory"]: ...
135
+ def __getitem__(self, index: slice) -> Sequence[AsyncFoamCase.TimeDirectory]: ...
136
136
 
137
137
  def __getitem__(
138
- self, index: Union[int, slice, float, str]
139
- ) -> Union["AsyncFoamCase.TimeDirectory", Sequence["AsyncFoamCase.TimeDirectory"]]:
138
+ self, index: int | slice | float | str
139
+ ) -> AsyncFoamCase.TimeDirectory | Sequence[AsyncFoamCase.TimeDirectory]:
140
140
  ret = super().__getitem__(index)
141
141
  if isinstance(ret, FoamCaseBase.TimeDirectory):
142
142
  return AsyncFoamCase.TimeDirectory(ret)
@@ -148,10 +148,10 @@ class AsyncFoamCase(FoamCaseRunBase):
148
148
 
149
149
  async def run(
150
150
  self,
151
- cmd: Optional[Union[Sequence[Union[str, "os.PathLike[str]"]], str]] = None,
151
+ cmd: Sequence[str | os.PathLike[str]] | str | None = None,
152
152
  *,
153
- parallel: Optional[bool] = None,
154
- cpus: Optional[int] = None,
153
+ parallel: bool | None = None,
154
+ cpus: int | None = None,
155
155
  check: bool = True,
156
156
  log: bool = True,
157
157
  ) -> None:
@@ -192,7 +192,7 @@ class AsyncFoamCase(FoamCaseRunBase):
192
192
  @awaitableasynccontextmanager
193
193
  @asynccontextmanager
194
194
  async def copy(
195
- self, dst: Optional[Union["os.PathLike[str]", str]] = None
195
+ self, dst: os.PathLike[str] | str | None = None
196
196
  ) -> AsyncGenerator[Self, None]:
197
197
  """
198
198
  Make a copy of this case.
@@ -213,7 +213,7 @@ class AsyncFoamCase(FoamCaseRunBase):
213
213
  @awaitableasynccontextmanager
214
214
  @asynccontextmanager
215
215
  async def clone(
216
- self, dst: Optional[Union["os.PathLike[str]", str]] = None
216
+ self, dst: os.PathLike[str] | str | None = None
217
217
  ) -> AsyncGenerator[Self, None]:
218
218
  """
219
219
  Clone this case (make a clean copy).
@@ -1,22 +1,19 @@
1
+ from __future__ import annotations
2
+
1
3
  import shutil
2
4
  import sys
3
5
  from pathlib import Path
4
- from typing import (
5
- TYPE_CHECKING,
6
- Optional,
7
- Union,
8
- overload,
9
- )
6
+ from typing import TYPE_CHECKING, overload
10
7
 
11
8
  if sys.version_info >= (3, 9):
12
9
  from collections.abc import (
13
10
  Iterator,
14
11
  Sequence,
15
- Set,
16
12
  )
13
+ from collections.abc import Set as AbstractSet
17
14
  else:
18
- from typing import AbstractSet as Set
19
15
  from typing import (
16
+ AbstractSet,
20
17
  Iterator,
21
18
  Sequence,
22
19
  )
@@ -28,10 +25,10 @@ if TYPE_CHECKING:
28
25
 
29
26
 
30
27
  class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
31
- def __init__(self, path: Union["os.PathLike[str]", str] = Path()):
28
+ def __init__(self, path: os.PathLike[str] | str = Path()) -> None:
32
29
  self.path = Path(path).absolute()
33
30
 
34
- class TimeDirectory(Set[FoamFieldFile]):
31
+ class TimeDirectory(AbstractSet[FoamFieldFile]):
35
32
  """
36
33
  An OpenFOAM time directory in a case.
37
34
 
@@ -40,11 +37,11 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
40
37
  :param path: The path to the time directory.
41
38
  """
42
39
 
43
- def __init__(self, path: Union["os.PathLike[str]", str]):
40
+ def __init__(self, path: os.PathLike[str] | str) -> None:
44
41
  self.path = Path(path).absolute()
45
42
 
46
43
  @property
47
- def _case(self) -> "FoamCaseBase":
44
+ def _case(self) -> FoamCaseBase:
48
45
  return FoamCaseBase(self.path.parent)
49
46
 
50
47
  @property
@@ -97,7 +94,7 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
97
94
  return str(self.path)
98
95
 
99
96
  @property
100
- def _times(self) -> Sequence["FoamCaseBase.TimeDirectory"]:
97
+ def _times(self) -> Sequence[FoamCaseBase.TimeDirectory]:
101
98
  times = []
102
99
  for p in self.path.iterdir():
103
100
  if p.is_dir():
@@ -113,29 +110,28 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
113
110
  return times
114
111
 
115
112
  @overload
116
- def __getitem__(
117
- self, index: Union[int, float, str]
118
- ) -> "FoamCaseBase.TimeDirectory": ...
113
+ def __getitem__(self, index: int | float | str) -> FoamCaseBase.TimeDirectory: ...
119
114
 
120
115
  @overload
121
- def __getitem__(self, index: slice) -> Sequence["FoamCaseBase.TimeDirectory"]: ...
116
+ def __getitem__(self, index: slice) -> Sequence[FoamCaseBase.TimeDirectory]: ...
122
117
 
123
118
  def __getitem__(
124
- self, index: Union[int, slice, float, str]
125
- ) -> Union["FoamCaseBase.TimeDirectory", Sequence["FoamCaseBase.TimeDirectory"]]:
119
+ self, index: int | slice | float | str
120
+ ) -> FoamCaseBase.TimeDirectory | Sequence[FoamCaseBase.TimeDirectory]:
126
121
  if isinstance(index, str):
127
122
  return FoamCaseBase.TimeDirectory(self.path / index)
128
123
  if isinstance(index, float):
129
124
  for time in self._times:
130
125
  if time.time == index:
131
126
  return time
132
- raise IndexError(f"Time {index} not found")
127
+ msg = f"Time {index} not found"
128
+ raise IndexError(msg)
133
129
  return self._times[index]
134
130
 
135
131
  def __len__(self) -> int:
136
132
  return len(self._times)
137
133
 
138
- def __delitem__(self, key: Union[int, float, str]) -> None:
134
+ def __delitem__(self, key: int | float | str) -> None:
139
135
  shutil.rmtree(self[key].path)
140
136
 
141
137
  @property
@@ -143,22 +139,24 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
143
139
  """The name of the case."""
144
140
  return self.path.name
145
141
 
146
- def file(self, path: Union["os.PathLike[str]", str]) -> FoamFile:
142
+ def file(self, path: os.PathLike[str] | str) -> FoamFile:
147
143
  """Return a FoamFile object for the given path in the case."""
148
144
  return FoamFile(self.path / path)
149
145
 
150
146
  @property
151
- def _nsubdomains(self) -> Optional[int]:
147
+ def _nsubdomains(self) -> int | None:
152
148
  """Return the number of subdomains as set in the decomposeParDict, or None if no decomposeParDict is found."""
153
149
  try:
154
150
  nsubdomains = self.decompose_par_dict["numberOfSubdomains"]
155
151
  if not isinstance(nsubdomains, int):
156
- raise TypeError(
152
+ msg = (
157
153
  f"numberOfSubdomains in {self.decompose_par_dict} is not an integer"
158
154
  )
159
- return nsubdomains
155
+ raise TypeError(msg)
160
156
  except FileNotFoundError:
161
157
  return None
158
+ else:
159
+ return nsubdomains
162
160
 
163
161
  @property
164
162
  def _nprocessors(self) -> int:
@@ -170,7 +168,8 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
170
168
  """The application name as set in the controlDict."""
171
169
  application = self.control_dict["application"]
172
170
  if not isinstance(application, str):
173
- raise TypeError(f"application in {self.control_dict} is not a string")
171
+ msg = f"application in {self.control_dict} is not a string"
172
+ raise TypeError(msg)
174
173
  return application
175
174
 
176
175
  @property
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import os
2
4
  import shlex
3
5
  import shutil
@@ -6,13 +8,7 @@ import tempfile
6
8
  from abc import abstractmethod
7
9
  from contextlib import contextmanager
8
10
  from pathlib import Path
9
- from typing import (
10
- IO,
11
- Any,
12
- Optional,
13
- Tuple,
14
- Union,
15
- )
11
+ from typing import IO, TYPE_CHECKING, Any
16
12
 
17
13
  if sys.version_info >= (3, 9):
18
14
  from collections.abc import (
@@ -22,11 +18,11 @@ if sys.version_info >= (3, 9):
22
18
  Generator,
23
19
  Mapping,
24
20
  Sequence,
25
- Set,
26
21
  )
22
+ from collections.abc import Set as AbstractSet
27
23
  else:
28
- from typing import AbstractSet as Set
29
24
  from typing import (
25
+ AbstractSet,
30
26
  Callable,
31
27
  Collection,
32
28
  Coroutine,
@@ -40,22 +36,24 @@ if sys.version_info >= (3, 11):
40
36
  else:
41
37
  from typing_extensions import Self
42
38
 
43
- from .._files import FoamFieldFile
44
39
  from ._base import FoamCaseBase
45
40
  from ._subprocess import DEVNULL, STDOUT
46
41
 
42
+ if TYPE_CHECKING:
43
+ from .._files import FoamFieldFile
44
+
47
45
 
48
46
  class FoamCaseRunBase(FoamCaseBase):
49
47
  class TimeDirectory(FoamCaseBase.TimeDirectory):
50
48
  @abstractmethod
51
49
  def cell_centers(
52
50
  self,
53
- ) -> Union[FoamFieldFile, Coroutine[None, None, FoamFieldFile]]:
51
+ ) -> FoamFieldFile | Coroutine[None, None, FoamFieldFile]:
54
52
  raise NotImplementedError
55
53
 
56
54
  @property
57
55
  @abstractmethod
58
- def _case(self) -> "FoamCaseRunBase":
56
+ def _case(self) -> FoamCaseRunBase:
59
57
  raise NotImplementedError
60
58
 
61
59
  def _cell_centers_calls(self) -> Generator[Any, None, FoamFieldFile]:
@@ -70,92 +68,91 @@ class FoamCaseRunBase(FoamCaseBase):
70
68
 
71
69
  return ret
72
70
 
73
- def __delitem__(self, key: Union[int, float, str]) -> None:
71
+ def __delitem__(self, key: int | float | str) -> None:
74
72
  shutil.rmtree(self[key].path)
75
73
 
76
74
  @staticmethod
77
75
  @abstractmethod
78
76
  def _run(
79
- cmd: Union[Sequence[Union[str, "os.PathLike[str]"]], str],
77
+ cmd: Sequence[str | os.PathLike[str]] | str,
80
78
  *,
81
79
  cpus: int,
82
80
  **kwargs: Any,
83
- ) -> Union[None, Coroutine[None, None, None]]:
81
+ ) -> None | Coroutine[None, None, None]:
84
82
  raise NotImplementedError
85
83
 
86
84
  @staticmethod
87
85
  @abstractmethod
88
86
  def _rmtree(
89
- path: Union["os.PathLike[str]", str], *, ignore_errors: bool = False
90
- ) -> Union[None, Coroutine[None, None, None]]:
87
+ path: os.PathLike[str] | str, *, ignore_errors: bool = False
88
+ ) -> None | Coroutine[None, None, None]:
91
89
  raise NotImplementedError
92
90
 
93
91
  @staticmethod
94
92
  @abstractmethod
95
93
  def _copytree(
96
- src: Union["os.PathLike[str]", str],
97
- dest: Union["os.PathLike[str]", str],
94
+ src: os.PathLike[str] | str,
95
+ dest: os.PathLike[str] | str,
98
96
  *,
99
97
  symlinks: bool = False,
100
- ignore: Optional[
101
- Callable[[Union["os.PathLike[str]", str], Collection[str]], Collection[str]]
102
- ] = None,
103
- ) -> Union[None, Coroutine[None, None, None]]:
98
+ ignore: Callable[[os.PathLike[str] | str, Collection[str]], Collection[str]]
99
+ | None = None,
100
+ ) -> None | Coroutine[None, None, None]:
104
101
  raise NotImplementedError
105
102
 
106
103
  @abstractmethod
107
- def clean(self, *, check: bool = False) -> Union[None, Coroutine[None, None, None]]:
104
+ def clean(self, *, check: bool = False) -> None | Coroutine[None, None, None]:
108
105
  raise NotImplementedError
109
106
 
110
107
  @abstractmethod
111
- def copy(self, dst: Optional[Union["os.PathLike[str]", str]] = None) -> Any:
108
+ def copy(self, dst: os.PathLike[str] | str | None = None) -> Any:
112
109
  raise NotImplementedError
113
110
 
114
111
  @abstractmethod
115
- def clone(self, dst: Optional[Union["os.PathLike[str]", str]] = None) -> Any:
112
+ def clone(self, dst: os.PathLike[str] | str | None = None) -> Any:
116
113
  raise NotImplementedError
117
114
 
118
115
  @abstractmethod
119
116
  def _prepare(
120
117
  self, *, check: bool = True, log: bool = True
121
- ) -> Union[None, Coroutine[None, None, None]]:
118
+ ) -> None | Coroutine[None, None, None]:
122
119
  raise NotImplementedError
123
120
 
124
121
  @abstractmethod
125
122
  def run(
126
123
  self,
127
- cmd: Optional[Union[Sequence[Union[str, "os.PathLike[str]"]], str]] = None,
124
+ cmd: Sequence[str | os.PathLike[str]] | str | None = None,
128
125
  *,
129
- parallel: Optional[bool] = None,
130
- cpus: Optional[int] = None,
126
+ parallel: bool | None = None,
127
+ cpus: int | None = None,
131
128
  check: bool = True,
132
129
  log: bool = True,
133
- ) -> Union[None, Coroutine[None, None, None]]:
130
+ ) -> None | Coroutine[None, None, None]:
134
131
  raise NotImplementedError
135
132
 
136
133
  @abstractmethod
137
134
  def block_mesh(
138
135
  self, *, check: bool = True, log: bool = True
139
- ) -> Union[None, Coroutine[None, None, None]]:
136
+ ) -> None | Coroutine[None, None, None]:
140
137
  raise NotImplementedError
141
138
 
142
139
  @abstractmethod
143
140
  def decompose_par(
144
141
  self, *, check: bool = True, log: bool = True
145
- ) -> Union[None, Coroutine[None, None, None]]:
142
+ ) -> None | Coroutine[None, None, None]:
146
143
  raise NotImplementedError
147
144
 
148
145
  @abstractmethod
149
146
  def reconstruct_par(
150
147
  self, *, check: bool = True, log: bool = True
151
- ) -> Union[None, Coroutine[None, None, None]]:
148
+ ) -> None | Coroutine[None, None, None]:
152
149
  raise NotImplementedError
153
150
 
154
151
  @abstractmethod
155
- def restore_0_dir(self) -> Union[None, Coroutine[None, None, None]]:
152
+ def restore_0_dir(self) -> None | Coroutine[None, None, None]:
156
153
  raise NotImplementedError
157
154
 
158
- def __clean_paths(self) -> Set[Path]:
155
+ def __clean_paths(self) -> AbstractSet[Path]:
159
156
  has_decompose_par_dict = (self.path / "system" / "decomposeParDict").is_file()
160
157
  has_block_mesh_dict = (self.path / "system" / "blockMeshDict").is_file()
161
158
 
@@ -186,18 +183,18 @@ class FoamCaseRunBase(FoamCaseBase):
186
183
 
187
184
  def __clone_ignore(
188
185
  self,
189
- ) -> Callable[[Union["os.PathLike[str]", str], Collection[str]], Collection[str]]:
186
+ ) -> Callable[[os.PathLike[str] | str, Collection[str]], Collection[str]]:
190
187
  clean_paths = self.__clean_paths()
191
188
 
192
189
  def ignore(
193
- path: Union["os.PathLike[str]", str], names: Collection[str]
190
+ path: os.PathLike[str] | str, names: Collection[str]
194
191
  ) -> Collection[str]:
195
192
  paths = {Path(path) / name for name in names}
196
193
  return {p.name for p in paths.intersection(clean_paths)}
197
194
 
198
195
  return ignore
199
196
 
200
- def __clean_script(self) -> Optional[Path]:
197
+ def __clean_script(self) -> Path | None:
201
198
  """Return the path to the (All)clean script, or None if no clean script is found."""
202
199
  clean = self.path / "clean"
203
200
  all_clean = self.path / "Allclean"
@@ -214,7 +211,7 @@ class FoamCaseRunBase(FoamCaseBase):
214
211
 
215
212
  return script
216
213
 
217
- def __prepare_script(self) -> Optional[Path]:
214
+ def __prepare_script(self) -> Path | None:
218
215
  """Return the path to the Allrun.pre script, or None if no prepare script is found."""
219
216
  script = self.path / "Allrun.pre"
220
217
 
@@ -226,7 +223,7 @@ class FoamCaseRunBase(FoamCaseBase):
226
223
 
227
224
  return script
228
225
 
229
- def __run_script(self, *, parallel: Optional[bool]) -> Optional[Path]:
226
+ def __run_script(self, *, parallel: bool | None) -> Path | None:
230
227
  """Return the path to the (All)run script, or None if no run script is found."""
231
228
  run = self.path / "run"
232
229
  run_parallel = self.path / "run-parallel"
@@ -242,9 +239,8 @@ class FoamCaseRunBase(FoamCaseBase):
242
239
  elif parallel is False:
243
240
  script = run if run.is_file() else all_run
244
241
  else:
245
- raise ValueError(
246
- "Both (All)run and (All)run-parallel scripts are present. Please specify parallel argument."
247
- )
242
+ msg = "Both (All)run and (All)run-parallel scripts are present. Please specify parallel argument."
243
+ raise ValueError(msg)
248
244
  else:
249
245
  script = run if run.is_file() else all_run
250
246
  elif parallel is not False and (
@@ -259,7 +255,7 @@ class FoamCaseRunBase(FoamCaseBase):
259
255
 
260
256
  return script
261
257
 
262
- def __env(self, *, shell: bool) -> Optional[Mapping[str, str]]:
258
+ def __env(self, *, shell: bool) -> Mapping[str, str] | None:
263
259
  sip_workaround = os.environ.get(
264
260
  "FOAM_LD_LIBRARY_PATH", ""
265
261
  ) and not os.environ.get("DYLD_LIBRARY_PATH", "")
@@ -278,8 +274,8 @@ class FoamCaseRunBase(FoamCaseBase):
278
274
 
279
275
  @contextmanager
280
276
  def __output(
281
- self, cmd: Union[Sequence[Union[str, "os.PathLike[str]"]], str], *, log: bool
282
- ) -> Generator[Tuple[Union[int, IO[bytes]], Union[int, IO[bytes]]], None, None]:
277
+ self, cmd: Sequence[str | os.PathLike[str]] | str, *, log: bool
278
+ ) -> Generator[tuple[int | IO[bytes], int | IO[bytes]], None, None]:
283
279
  if log:
284
280
  if isinstance(cmd, str):
285
281
  name = shlex.split(cmd)[0]
@@ -299,7 +295,7 @@ class FoamCaseRunBase(FoamCaseBase):
299
295
  return ret
300
296
 
301
297
  def _copy_calls(
302
- self, dst: Optional[Union["os.PathLike[str]", str]]
298
+ self, dst: os.PathLike[str] | str | None
303
299
  ) -> Generator[Any, None, Self]:
304
300
  if dst is None:
305
301
  dst = self.__mkrundir()
@@ -321,7 +317,7 @@ class FoamCaseRunBase(FoamCaseBase):
321
317
  p.unlink()
322
318
 
323
319
  def _clone_calls(
324
- self, dst: Optional[Union["os.PathLike[str]", str]]
320
+ self, dst: os.PathLike[str] | str | None
325
321
  ) -> Generator[Any, None, Self]:
326
322
  if dst is None:
327
323
  dst = self.__mkrundir()
@@ -366,10 +362,10 @@ class FoamCaseRunBase(FoamCaseBase):
366
362
 
367
363
  def _run_calls(
368
364
  self,
369
- cmd: Optional[Union[Sequence[Union[str, "os.PathLike[str]"]], str]] = None,
365
+ cmd: Sequence[str | os.PathLike[str]] | str | None = None,
370
366
  *,
371
- parallel: Optional[bool],
372
- cpus: Optional[int],
367
+ parallel: bool | None,
368
+ cpus: int | None,
373
369
  check: bool,
374
370
  log: bool,
375
371
  **kwargs: Any,
@@ -420,9 +416,8 @@ class FoamCaseRunBase(FoamCaseBase):
420
416
  cpus = self._nsubdomains
421
417
  else:
422
418
  cpus = 1
423
- else:
424
- if cpus is None:
425
- cpus = 1
419
+ elif cpus is None:
420
+ cpus = 1
426
421
 
427
422
  yield self.run(
428
423
  [script_path], parallel=False, cpus=cpus, check=check, **kwargs
@@ -450,9 +445,8 @@ class FoamCaseRunBase(FoamCaseBase):
450
445
 
451
446
  if cpus is None:
452
447
  cpus = max(self._nprocessors, 1)
453
- else:
454
- if cpus is None:
455
- cpus = 1
448
+ elif cpus is None:
449
+ cpus = 1
456
450
 
457
451
  yield self.run(
458
452
  [self.application],
@@ -1,6 +1,8 @@
1
+ from __future__ import annotations
2
+
1
3
  import shutil
2
4
  import sys
3
- from typing import TYPE_CHECKING, Any, Optional, Union
5
+ from typing import TYPE_CHECKING, Any
4
6
 
5
7
  if sys.version_info >= (3, 9):
6
8
  from collections.abc import Sequence
@@ -19,7 +21,7 @@ class AsyncSlurmFoamCase(AsyncFoamCase):
19
21
 
20
22
  @staticmethod
21
23
  async def _run(
22
- cmd: Union[Sequence[Union[str, "os.PathLike[str]"]], str],
24
+ cmd: Sequence[str | os.PathLike[str]] | str,
23
25
  *,
24
26
  cpus: int,
25
27
  fallback: bool = False,
@@ -42,10 +44,10 @@ class AsyncSlurmFoamCase(AsyncFoamCase):
42
44
 
43
45
  async def run(
44
46
  self,
45
- cmd: Optional[Union[Sequence[Union[str, "os.PathLike[str]"]], str]] = None,
47
+ cmd: Sequence[str | os.PathLike[str]] | str | None = None,
46
48
  *,
47
- parallel: Optional[bool] = None,
48
- cpus: Optional[int] = None,
49
+ parallel: bool | None = None,
50
+ cpus: int | None = None,
49
51
  check: bool = True,
50
52
  log: bool = True,
51
53
  fallback: bool = False,
@@ -1,8 +1,10 @@
1
+ from __future__ import annotations
2
+
1
3
  import asyncio
2
4
  import subprocess
3
5
  import sys
4
6
  from io import BytesIO
5
- from typing import IO, TYPE_CHECKING, Optional, Union
7
+ from typing import IO, TYPE_CHECKING
6
8
 
7
9
  if TYPE_CHECKING:
8
10
  import os
@@ -31,14 +33,14 @@ STDOUT = subprocess.STDOUT
31
33
 
32
34
 
33
35
  def run_sync(
34
- cmd: Union[Sequence[Union[str, "os.PathLike[str]"]], str],
36
+ cmd: Sequence[str | os.PathLike[str]] | str,
35
37
  *,
36
38
  check: bool = True,
37
- cwd: Optional["os.PathLike[str]"] = None,
38
- env: Optional[Mapping[str, str]] = None,
39
- stdout: Optional[Union[int, IO[bytes]]] = None,
40
- stderr: Optional[Union[int, IO[bytes]]] = None,
41
- ) -> "CompletedProcess[bytes]":
39
+ cwd: os.PathLike[str] | None = None,
40
+ env: Mapping[str, str] | None = None,
41
+ stdout: int | IO[bytes] | None = None,
42
+ stderr: int | IO[bytes] | None = None,
43
+ ) -> CompletedProcess[bytes]:
42
44
  if not isinstance(cmd, str) and sys.version_info < (3, 8):
43
45
  cmd = [str(arg) for arg in cmd]
44
46
 
@@ -87,14 +89,14 @@ def run_sync(
87
89
 
88
90
 
89
91
  async def run_async(
90
- cmd: Union[Sequence[Union[str, "os.PathLike[str]"]], str],
92
+ cmd: Sequence[str | os.PathLike[str]] | str,
91
93
  *,
92
94
  check: bool = True,
93
- cwd: Optional["os.PathLike[str]"] = None,
94
- env: Optional[Mapping[str, str]] = None,
95
- stdout: Optional[Union[int, IO[bytes]]] = None,
96
- stderr: Optional[Union[int, IO[bytes]]] = None,
97
- ) -> "CompletedProcess[bytes]":
95
+ cwd: os.PathLike[str] | None = None,
96
+ env: Mapping[str, str] | None = None,
97
+ stdout: int | IO[bytes] | None = None,
98
+ stderr: int | IO[bytes] | None = None,
99
+ ) -> CompletedProcess[bytes]:
98
100
  if isinstance(cmd, str):
99
101
  proc = await asyncio.create_subprocess_shell(
100
102
  cmd,