foamlib 0.6.11__py3-none-any.whl → 0.6.13__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.
foamlib/_cases/_sync.py CHANGED
@@ -1,7 +1,8 @@
1
+ from __future__ import annotations
2
+
1
3
  import shutil
2
4
  import sys
3
- from types import TracebackType
4
- from typing import TYPE_CHECKING, Any, Callable, Optional, Type, Union, overload
5
+ from typing import TYPE_CHECKING, Any, Callable, overload
5
6
 
6
7
  if sys.version_info >= (3, 9):
7
8
  from collections.abc import Collection, Sequence
@@ -13,7 +14,6 @@ if sys.version_info >= (3, 11):
13
14
  else:
14
15
  from typing_extensions import Self
15
16
 
16
- from .._files import FoamFieldFile
17
17
  from ._base import FoamCaseBase
18
18
  from ._run import FoamCaseRunBase
19
19
  from ._subprocess import run_sync
@@ -21,6 +21,9 @@ from ._util import ValuedGenerator
21
21
 
22
22
  if TYPE_CHECKING:
23
23
  import os
24
+ from types import TracebackType
25
+
26
+ from .._files import FoamFieldFile
24
27
 
25
28
 
26
29
  class FoamCase(FoamCaseRunBase):
@@ -36,7 +39,7 @@ class FoamCase(FoamCaseRunBase):
36
39
 
37
40
  class TimeDirectory(FoamCaseRunBase.TimeDirectory):
38
41
  @property
39
- def _case(self) -> "FoamCase":
42
+ def _case(self) -> FoamCase:
40
43
  return FoamCase(self.path.parent)
41
44
 
42
45
  def cell_centers(self) -> FoamFieldFile:
@@ -50,7 +53,7 @@ class FoamCase(FoamCaseRunBase):
50
53
 
51
54
  @staticmethod
52
55
  def _run(
53
- cmd: Union[Sequence[Union[str, "os.PathLike[str]"]], str],
56
+ cmd: Sequence[str | os.PathLike[str]] | str,
54
57
  *,
55
58
  cpus: int,
56
59
  **kwargs: Any,
@@ -58,34 +61,29 @@ class FoamCase(FoamCaseRunBase):
58
61
  run_sync(cmd, **kwargs)
59
62
 
60
63
  @staticmethod
61
- def _rmtree(
62
- path: Union["os.PathLike[str]", str], *, ignore_errors: bool = False
63
- ) -> None:
64
+ def _rmtree(path: os.PathLike[str] | str, *, ignore_errors: bool = False) -> None:
64
65
  shutil.rmtree(path, ignore_errors=ignore_errors)
65
66
 
66
67
  @staticmethod
67
68
  def _copytree(
68
- src: Union["os.PathLike[str]", str],
69
- dest: Union["os.PathLike[str]", str],
69
+ src: os.PathLike[str] | str,
70
+ dest: os.PathLike[str] | str,
70
71
  *,
71
72
  symlinks: bool = False,
72
- ignore: Optional[
73
- Callable[[Union["os.PathLike[str]", str], Collection[str]], Collection[str]]
74
- ] = None,
73
+ ignore: Callable[[os.PathLike[str] | str, Collection[str]], Collection[str]]
74
+ | None = None,
75
75
  ) -> None:
76
76
  shutil.copytree(src, dest, symlinks=symlinks, ignore=ignore)
77
77
 
78
78
  @overload
79
- def __getitem__(
80
- self, index: Union[int, float, str]
81
- ) -> "FoamCase.TimeDirectory": ...
79
+ def __getitem__(self, index: int | float | str) -> FoamCase.TimeDirectory: ...
82
80
 
83
81
  @overload
84
- def __getitem__(self, index: slice) -> Sequence["FoamCase.TimeDirectory"]: ...
82
+ def __getitem__(self, index: slice) -> Sequence[FoamCase.TimeDirectory]: ...
85
83
 
86
84
  def __getitem__(
87
- self, index: Union[int, slice, float, str]
88
- ) -> Union["FoamCase.TimeDirectory", Sequence["FoamCase.TimeDirectory"]]:
85
+ self, index: int | slice | float | str
86
+ ) -> FoamCase.TimeDirectory | Sequence[FoamCase.TimeDirectory]:
89
87
  ret = super().__getitem__(index)
90
88
  if isinstance(ret, FoamCaseBase.TimeDirectory):
91
89
  return FoamCase.TimeDirectory(ret)
@@ -96,9 +94,9 @@ class FoamCase(FoamCaseRunBase):
96
94
 
97
95
  def __exit__(
98
96
  self,
99
- exc_type: Optional[Type[BaseException]],
100
- exc_val: Optional[BaseException],
101
- exc_tb: Optional[TracebackType],
97
+ exc_type: type[BaseException] | None,
98
+ exc_val: BaseException | None,
99
+ exc_tb: TracebackType | None,
102
100
  ) -> None:
103
101
  self._rmtree(self.path)
104
102
 
@@ -117,10 +115,10 @@ class FoamCase(FoamCaseRunBase):
117
115
 
118
116
  def run(
119
117
  self,
120
- cmd: Optional[Union[Sequence[Union[str, "os.PathLike[str]"]], str]] = None,
118
+ cmd: Sequence[str | os.PathLike[str]] | str | None = None,
121
119
  *,
122
- parallel: Optional[bool] = None,
123
- cpus: Optional[int] = None,
120
+ parallel: bool | None = None,
121
+ cpus: int | None = None,
124
122
  check: bool = True,
125
123
  log: bool = True,
126
124
  ) -> None:
@@ -158,7 +156,7 @@ class FoamCase(FoamCaseRunBase):
158
156
  for _ in self._restore_0_dir_calls():
159
157
  pass
160
158
 
161
- def copy(self, dst: Optional[Union["os.PathLike[str]", str]] = None) -> Self:
159
+ def copy(self, dst: os.PathLike[str] | str | None = None) -> Self:
162
160
  """
163
161
  Make a copy of this case.
164
162
 
@@ -173,7 +171,7 @@ class FoamCase(FoamCaseRunBase):
173
171
 
174
172
  return calls.value
175
173
 
176
- def clone(self, dst: Optional[Union["os.PathLike[str]", str]] = None) -> Self:
174
+ def clone(self, dst: os.PathLike[str] | str | None = None) -> Self:
177
175
  """
178
176
  Clone this case (make a clean copy).
179
177
 
foamlib/_cases/_util.py CHANGED
@@ -1,16 +1,19 @@
1
+ from __future__ import annotations
2
+
1
3
  import functools
2
4
  import sys
3
- from types import TracebackType
4
5
  from typing import (
6
+ TYPE_CHECKING,
5
7
  Any,
6
8
  AsyncContextManager,
7
9
  Callable,
8
10
  Generic,
9
- Optional,
10
- Type,
11
11
  TypeVar,
12
12
  )
13
13
 
14
+ if TYPE_CHECKING:
15
+ from types import TracebackType
16
+
14
17
  if sys.version_info >= (3, 9):
15
18
  from collections.abc import Generator
16
19
  else:
@@ -23,7 +26,7 @@ R = TypeVar("R")
23
26
 
24
27
 
25
28
  class ValuedGenerator(Generic[Y, S, R]):
26
- def __init__(self, generator: Generator[Y, S, R]):
29
+ def __init__(self, generator: Generator[Y, S, R]) -> None:
27
30
  self._generator = generator
28
31
 
29
32
  def __iter__(self) -> Generator[Y, S, R]:
@@ -32,7 +35,7 @@ class ValuedGenerator(Generic[Y, S, R]):
32
35
 
33
36
 
34
37
  class _AwaitableAsyncContextManager(Generic[R]):
35
- def __init__(self, cm: "AsyncContextManager[R]"):
38
+ def __init__(self, cm: AsyncContextManager[R]) -> None:
36
39
  self._cm = cm
37
40
 
38
41
  def __await__(self) -> Generator[Any, Any, R]:
@@ -43,15 +46,15 @@ class _AwaitableAsyncContextManager(Generic[R]):
43
46
 
44
47
  async def __aexit__(
45
48
  self,
46
- exc_type: Optional[Type[BaseException]],
47
- exc_val: Optional[BaseException],
48
- exc_tb: Optional[TracebackType],
49
- ) -> Optional[bool]:
49
+ exc_type: type[BaseException] | None,
50
+ exc_val: BaseException | None,
51
+ exc_tb: TracebackType | None,
52
+ ) -> bool | None:
50
53
  return await self._cm.__aexit__(exc_type, exc_val, exc_tb)
51
54
 
52
55
 
53
56
  def awaitableasynccontextmanager(
54
- cm: Callable[..., "AsyncContextManager[R]"],
57
+ cm: Callable[..., AsyncContextManager[R]],
55
58
  ) -> Callable[..., _AwaitableAsyncContextManager[R]]:
56
59
  @functools.wraps(cm)
57
60
  def f(*args: Any, **kwargs: Any) -> _AwaitableAsyncContextManager[R]:
foamlib/_files/_base.py CHANGED
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import sys
2
4
  from dataclasses import dataclass
3
5
  from typing import TYPE_CHECKING, Dict, NamedTuple, Optional, Tuple, Union
@@ -33,9 +35,9 @@ class FoamFileBase:
33
35
 
34
36
  @dataclass
35
37
  class Dimensioned:
36
- value: "FoamFileBase._Tensor" = 0
37
- dimensions: Union["FoamFileBase.DimensionSet", Sequence[float]] = ()
38
- name: Optional[str] = None
38
+ value: FoamFileBase._Tensor = 0
39
+ dimensions: FoamFileBase.DimensionSet | Sequence[float] = ()
40
+ name: str | None = None
39
41
 
40
42
  def __post_init__(self) -> None:
41
43
  if not isinstance(self.dimensions, FoamFileBase.DimensionSet):
foamlib/_files/_files.py CHANGED
@@ -1,3 +1,6 @@
1
+ from __future__ import annotations
2
+
3
+ import os
1
4
  import sys
2
5
  from copy import deepcopy
3
6
  from typing import Any, Optional, Tuple, Union, cast
@@ -14,7 +17,7 @@ else:
14
17
 
15
18
  from ._base import FoamFileBase
16
19
  from ._io import FoamFileIO
17
- from ._serialization import Kind, dumps
20
+ from ._serialization import Kind, dumps, normalize
18
21
  from ._util import is_sequence
19
22
 
20
23
 
@@ -39,13 +42,13 @@ class FoamFile(
39
42
  ):
40
43
  """An OpenFOAM dictionary within a file as a mutable mapping."""
41
44
 
42
- def __init__(self, _file: "FoamFile", _keywords: Tuple[str, ...]) -> None:
45
+ def __init__(self, _file: FoamFile, _keywords: tuple[str, ...]) -> None:
43
46
  self._file = _file
44
47
  self._keywords = _keywords
45
48
 
46
49
  def __getitem__(
47
50
  self, keyword: str
48
- ) -> Union[FoamFileBase._DataEntry, "FoamFile.SubDict"]:
51
+ ) -> FoamFileBase._DataEntry | FoamFile.SubDict:
49
52
  return self._file[(*self._keywords, keyword)]
50
53
 
51
54
  def __setitem__(
@@ -97,7 +100,8 @@ class FoamFile(
97
100
  """Alias of `self["FoamFile", "version"]`."""
98
101
  ret = self["FoamFile", "version"]
99
102
  if not isinstance(ret, (int, float)):
100
- raise TypeError("version is not a number")
103
+ msg = "version is not a number"
104
+ raise TypeError(msg)
101
105
  return ret
102
106
 
103
107
  @version.setter
@@ -109,9 +113,11 @@ class FoamFile(
109
113
  """Alias of `self["FoamFile", "format"]`."""
110
114
  ret = self["FoamFile", "format"]
111
115
  if not isinstance(ret, str):
112
- raise TypeError("format is not a string")
116
+ msg = "format is not a string"
117
+ raise TypeError(msg)
113
118
  if ret not in ("ascii", "binary"):
114
- raise ValueError("format is not 'ascii' or 'binary'")
119
+ msg = "format is not 'ascii' or 'binary'"
120
+ raise ValueError(msg)
115
121
  return cast(Literal["ascii", "binary"], ret)
116
122
 
117
123
  @format.setter
@@ -123,7 +129,8 @@ class FoamFile(
123
129
  """Alias of `self["FoamFile", "class"]`."""
124
130
  ret = self["FoamFile", "class"]
125
131
  if not isinstance(ret, str):
126
- raise TypeError("class is not a string")
132
+ msg = "class is not a string"
133
+ raise TypeError(msg)
127
134
  return ret
128
135
 
129
136
  @class_.setter
@@ -135,7 +142,8 @@ class FoamFile(
135
142
  """Alias of `self["FoamFile", "location"]`."""
136
143
  ret = self["FoamFile", "location"]
137
144
  if not isinstance(ret, str):
138
- raise TypeError("location is not a string")
145
+ msg = "location is not a string"
146
+ raise TypeError(msg)
139
147
  return ret
140
148
 
141
149
  @location.setter
@@ -147,7 +155,8 @@ class FoamFile(
147
155
  """Alias of `self["FoamFile", "object"]`."""
148
156
  ret = self["FoamFile", "object"]
149
157
  if not isinstance(ret, str):
150
- raise TypeError("object is not a string")
158
+ msg = "object is not a string"
159
+ raise TypeError(msg)
151
160
  return ret
152
161
 
153
162
  @object_.setter
@@ -155,8 +164,8 @@ class FoamFile(
155
164
  self["FoamFile", "object"] = value
156
165
 
157
166
  def __getitem__(
158
- self, keywords: Optional[Union[str, Tuple[str, ...]]]
159
- ) -> Union[FoamFileBase._DataEntry, "FoamFile.SubDict"]:
167
+ self, keywords: str | tuple[str, ...] | None
168
+ ) -> FoamFileBase._DataEntry | FoamFile.SubDict:
160
169
  if not keywords:
161
170
  keywords = ()
162
171
  elif not isinstance(keywords, tuple):
@@ -173,14 +182,14 @@ class FoamFile(
173
182
  return deepcopy(value)
174
183
 
175
184
  def __setitem__(
176
- self, keywords: Optional[Union[str, Tuple[str, ...]]], data: FoamFileBase.Data
185
+ self, keywords: str | tuple[str, ...] | None, data: FoamFileBase.Data
177
186
  ) -> None:
178
- with self:
179
- if not keywords:
180
- keywords = ()
181
- elif not isinstance(keywords, tuple):
182
- keywords = (keywords,)
187
+ if not keywords:
188
+ keywords = ()
189
+ elif not isinstance(keywords, tuple):
190
+ keywords = (keywords,)
183
191
 
192
+ with self:
184
193
  try:
185
194
  write_header = (
186
195
  not self and "FoamFile" not in self and keywords != ("FoamFile",)
@@ -208,92 +217,115 @@ class FoamFile(
208
217
  or keywords[2].endswith("Gradient")
209
218
  )
210
219
  ):
211
- kind = Kind.BINARY_FIELD if self.format == "binary" else Kind.FIELD
220
+ if self.format == "binary":
221
+ arch = self.get(("FoamFile", "arch"), default=None)
222
+ assert arch is None or isinstance(arch, str)
223
+ if (arch is not None and "scalar=32" in arch) or (
224
+ arch is None
225
+ and os.environ.get("WM_PRECISION_OPTION", default="DP") == "SP"
226
+ ):
227
+ kind = Kind.SINGLE_PRECISION_BINARY_FIELD
228
+ else:
229
+ kind = Kind.DOUBLE_PRECISION_BINARY_FIELD
230
+ else:
231
+ kind = Kind.ASCII_FIELD
212
232
  elif keywords == ("dimensions",):
213
233
  kind = Kind.DIMENSIONS
214
234
 
215
235
  if (
216
- kind == Kind.FIELD or kind == Kind.BINARY_FIELD
236
+ kind
237
+ in (
238
+ Kind.ASCII_FIELD,
239
+ Kind.DOUBLE_PRECISION_BINARY_FIELD,
240
+ Kind.SINGLE_PRECISION_BINARY_FIELD,
241
+ )
217
242
  ) and self.class_ == "dictionary":
218
- if not is_sequence(data):
219
- class_ = "volScalarField"
220
- elif (len(data) == 3 and not is_sequence(data[0])) or len(data[0]) == 3:
221
- class_ = "volVectorField"
222
- elif (len(data) == 6 and not is_sequence(data[0])) or len(data[0]) == 6:
223
- class_ = "volSymmTensorField"
224
- elif (len(data) == 9 and not is_sequence(data[0])) or len(data[0]) == 9:
225
- class_ = "volTensorField"
243
+ if isinstance(data, (int, float)):
244
+ self.class_ = "volScalarField"
245
+
246
+ elif is_sequence(data) and data:
247
+ if isinstance(data[0], (int, float)):
248
+ if len(data) == 3:
249
+ self.class_ = "volVectorField"
250
+ elif len(data) == 6:
251
+ self.class_ = "volSymmTensorField"
252
+ elif len(data) == 9:
253
+ self.class_ = "volTensorField"
254
+ elif (
255
+ is_sequence(data[0])
256
+ and data[0]
257
+ and isinstance(data[0][0], (int, float))
258
+ ):
259
+ if len(data[0]) == 3:
260
+ self.class_ = "volVectorField"
261
+ elif len(data[0]) == 6:
262
+ self.class_ = "volSymmTensorField"
263
+ elif len(data[0]) == 9:
264
+ self.class_ = "volTensorField"
265
+
266
+ parsed = self._get_parsed(missing_ok=True)
267
+
268
+ start, end = parsed.entry_location(keywords, missing_ok=True)
269
+
270
+ if start and not parsed.contents[:start].endswith(b"\n\n"):
271
+ if parsed.contents[:start].endswith(b"\n"):
272
+ before = b"\n" if len(keywords) <= 1 else b""
226
273
  else:
227
- class_ = "volScalarField"
228
-
229
- self.class_ = class_
230
- self[keywords] = data
231
-
274
+ before = b"\n\n" if len(keywords) <= 1 else b"\n"
232
275
  else:
233
- parsed = self._get_parsed(missing_ok=True)
234
-
235
- start, end = parsed.entry_location(keywords, missing_ok=True)
236
-
237
276
  before = b""
238
- if parsed.contents[:start] and not parsed.contents[:start].endswith(
239
- b"\n"
240
- ):
241
- before = b"\n"
242
- if (
243
- parsed.contents[:start]
244
- and len(keywords) <= 1
245
- and not parsed.contents[:start].endswith(b"\n\n")
246
- ):
247
- before = b"\n\n"
248
277
 
278
+ if not parsed.contents[end:].strip() or parsed.contents[end:].startswith(
279
+ b"}"
280
+ ):
281
+ after = b"\n" + b" " * (len(keywords) - 2)
282
+ else:
249
283
  after = b""
250
- if parsed.contents[end:].startswith(b"}"):
251
- after = b" " * (len(keywords) - 2)
252
- if not parsed.contents[end:] or not parsed.contents[end:].startswith(
253
- b"\n"
254
- ):
255
- after = b"\n" + after
256
-
257
- indentation = b" " * (len(keywords) - 1)
258
-
259
- if isinstance(data, Mapping):
260
- if isinstance(data, (FoamFile, FoamFile.SubDict)):
261
- data = data.as_dict()
262
-
263
- parsed.put(
264
- keywords,
265
- ...,
266
- before
267
- + indentation
268
- + dumps(keywords[-1])
269
- + b"\n"
270
- + indentation
271
- + b"{\n"
272
- + indentation
273
- + b"}"
274
- + after,
275
- )
276
-
277
- for k, v in data.items():
278
- self[(*keywords, k)] = v
279
-
280
- elif keywords:
281
- parsed.put(
282
- keywords,
283
- data,
284
- before
285
- + indentation
286
- + dumps(keywords[-1])
287
- + b" "
288
- + dumps(data, kind=kind)
289
- + b";"
290
- + after,
291
- )
292
284
 
293
- else:
294
- parsed.put(keywords, data, before + dumps(data, kind=kind) + after)
285
+ indentation = b" " * (len(keywords) - 1)
286
+
287
+ if isinstance(data, Mapping):
288
+ if isinstance(data, (FoamFile, FoamFile.SubDict)):
289
+ data = data.as_dict()
290
+
291
+ parsed.put(
292
+ keywords,
293
+ ...,
294
+ before
295
+ + indentation
296
+ + dumps(keywords[-1])
297
+ + b"\n"
298
+ + indentation
299
+ + b"{\n"
300
+ + indentation
301
+ + b"}"
302
+ + after,
303
+ )
304
+
305
+ for k, v in data.items():
306
+ self[(*keywords, k)] = v
307
+
308
+ elif keywords:
309
+ parsed.put(
310
+ keywords,
311
+ normalize(data, kind=kind),
312
+ before
313
+ + indentation
314
+ + dumps(keywords[-1])
315
+ + b" "
316
+ + dumps(data, kind=kind)
317
+ + b";"
318
+ + after,
319
+ )
320
+
321
+ else:
322
+ parsed.put(
323
+ (),
324
+ normalize(data, kind=kind),
325
+ before + dumps(data, kind=kind) + after,
326
+ )
295
327
 
296
- def __delitem__(self, keywords: Optional[Union[str, Tuple[str, ...]]]) -> None:
328
+ def __delitem__(self, keywords: str | tuple[str, ...] | None) -> None:
297
329
  if not keywords:
298
330
  keywords = ()
299
331
  elif not isinstance(keywords, tuple):
@@ -302,12 +334,12 @@ class FoamFile(
302
334
  with self:
303
335
  del self._get_parsed()[keywords]
304
336
 
305
- def _iter(self, keywords: Tuple[str, ...] = ()) -> Iterator[Optional[str]]:
337
+ def _iter(self, keywords: tuple[str, ...] = ()) -> Iterator[str | None]:
306
338
  yield from (
307
339
  k[-1] if k else None for k in self._get_parsed() if k[:-1] == keywords
308
340
  )
309
341
 
310
- def __iter__(self) -> Iterator[Optional[str]]:
342
+ def __iter__(self) -> Iterator[str | None]:
311
343
  yield from (k for k in self._iter() if k != "FoamFile")
312
344
 
313
345
  def __contains__(self, keywords: object) -> bool:
@@ -348,11 +380,12 @@ class FoamFieldFile(FoamFile):
348
380
  """An OpenFOAM dictionary file representing a field as a mutable mapping."""
349
381
 
350
382
  class BoundariesSubDict(FoamFile.SubDict):
351
- def __getitem__(self, keyword: str) -> "FoamFieldFile.BoundarySubDict":
383
+ def __getitem__(self, keyword: str) -> FoamFieldFile.BoundarySubDict:
352
384
  value = super().__getitem__(keyword)
353
385
  if not isinstance(value, FoamFieldFile.BoundarySubDict):
354
386
  assert not isinstance(value, FoamFile.SubDict)
355
- raise TypeError(f"boundary {keyword} is not a dictionary")
387
+ msg = f"boundary {keyword} is not a dictionary"
388
+ raise TypeError(msg)
356
389
  return value
357
390
 
358
391
  class BoundarySubDict(FoamFile.SubDict):
@@ -363,7 +396,8 @@ class FoamFieldFile(FoamFile):
363
396
  """Alias of `self["type"]`."""
364
397
  ret = self["type"]
365
398
  if not isinstance(ret, str):
366
- raise TypeError("type is not a string")
399
+ msg = "type is not a string"
400
+ raise TypeError(msg)
367
401
  return ret
368
402
 
369
403
  @type.setter
@@ -392,8 +426,8 @@ class FoamFieldFile(FoamFile):
392
426
  del self["value"]
393
427
 
394
428
  def __getitem__(
395
- self, keywords: Optional[Union[str, Tuple[str, ...]]]
396
- ) -> Union[FoamFileBase._DataEntry, FoamFile.SubDict]:
429
+ self, keywords: str | tuple[str, ...] | None
430
+ ) -> FoamFileBase._DataEntry | FoamFile.SubDict:
397
431
  if not keywords:
398
432
  keywords = ()
399
433
  elif not isinstance(keywords, tuple):
@@ -408,15 +442,16 @@ class FoamFieldFile(FoamFile):
408
442
  return ret
409
443
 
410
444
  @property
411
- def dimensions(self) -> Union[FoamFile.DimensionSet, Sequence[float]]:
445
+ def dimensions(self) -> FoamFile.DimensionSet | Sequence[float]:
412
446
  """Alias of `self["dimensions"]`."""
413
447
  ret = self["dimensions"]
414
448
  if not isinstance(ret, FoamFile.DimensionSet):
415
- raise TypeError("dimensions is not a DimensionSet")
449
+ msg = "dimensions is not a DimensionSet"
450
+ raise TypeError(msg)
416
451
  return ret
417
452
 
418
453
  @dimensions.setter
419
- def dimensions(self, value: Union[FoamFile.DimensionSet, Sequence[float]]) -> None:
454
+ def dimensions(self, value: FoamFile.DimensionSet | Sequence[float]) -> None:
420
455
  self["dimensions"] = value
421
456
 
422
457
  @property
@@ -434,12 +469,13 @@ class FoamFieldFile(FoamFile):
434
469
  self["internalField"] = value
435
470
 
436
471
  @property
437
- def boundary_field(self) -> "FoamFieldFile.BoundariesSubDict":
472
+ def boundary_field(self) -> FoamFieldFile.BoundariesSubDict:
438
473
  """Alias of `self["boundaryField"]`."""
439
474
  ret = self["boundaryField"]
440
475
  if not isinstance(ret, FoamFieldFile.BoundariesSubDict):
441
476
  assert not isinstance(ret, FoamFile.SubDict)
442
- raise TypeError("boundaryField is not a dictionary")
477
+ msg = "boundaryField is not a dictionary"
478
+ raise TypeError(msg)
443
479
  return ret
444
480
 
445
481
  @boundary_field.setter
foamlib/_files/_io.py CHANGED
@@ -1,12 +1,10 @@
1
+ from __future__ import annotations
2
+
1
3
  import gzip
2
4
  import sys
3
5
  from pathlib import Path
4
- from types import TracebackType
5
6
  from typing import (
6
7
  TYPE_CHECKING,
7
- Optional,
8
- Type,
9
- Union,
10
8
  )
11
9
 
12
10
  if sys.version_info >= (3, 11):
@@ -18,14 +16,15 @@ from ._parsing import Parsed
18
16
 
19
17
  if TYPE_CHECKING:
20
18
  import os
19
+ from types import TracebackType
21
20
 
22
21
 
23
22
  class FoamFileIO:
24
- def __init__(self, path: Union["os.PathLike[str]", str]) -> None:
23
+ def __init__(self, path: os.PathLike[str] | str) -> None:
25
24
  self.path = Path(path).absolute()
26
25
 
27
- self.__parsed: Optional[Parsed] = None
28
- self.__missing: Optional[bool] = None
26
+ self.__parsed: Parsed | None = None
27
+ self.__missing: bool | None = None
29
28
  self.__defer_io = 0
30
29
 
31
30
  def __enter__(self) -> Self:
@@ -36,9 +35,9 @@ class FoamFileIO:
36
35
 
37
36
  def __exit__(
38
37
  self,
39
- exc_type: Optional[Type[BaseException]],
40
- exc_val: Optional[BaseException],
41
- exc_tb: Optional[TracebackType],
38
+ exc_type: type[BaseException] | None,
39
+ exc_val: BaseException | None,
40
+ exc_tb: TracebackType | None,
42
41
  ) -> None:
43
42
  self.__defer_io -= 1
44
43
  if self.__defer_io == 0: