foamlib 1.4.2__tar.gz → 1.4.4__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 (35) hide show
  1. {foamlib-1.4.2 → foamlib-1.4.4}/PKG-INFO +2 -2
  2. {foamlib-1.4.2 → foamlib-1.4.4}/README.md +1 -1
  3. {foamlib-1.4.2 → foamlib-1.4.4}/pyproject.toml +1 -1
  4. {foamlib-1.4.2 → foamlib-1.4.4}/src/foamlib/_cases/base.py +1 -1
  5. {foamlib-1.4.2 → foamlib-1.4.4}/src/foamlib/_files/_parsing/_elements.py +61 -40
  6. {foamlib-1.4.2 → foamlib-1.4.4}/src/foamlib/_files/_parsing/parsing.py +80 -4
  7. {foamlib-1.4.2 → foamlib-1.4.4}/src/foamlib/_files/_serialization.py +5 -15
  8. {foamlib-1.4.2 → foamlib-1.4.4}/src/foamlib/_files/_typing.py +25 -4
  9. {foamlib-1.4.2 → foamlib-1.4.4}/src/foamlib/_files/files.py +240 -220
  10. {foamlib-1.4.2 → foamlib-1.4.4}/LICENSE.txt +0 -0
  11. {foamlib-1.4.2 → foamlib-1.4.4}/src/foamlib/__init__.py +0 -0
  12. {foamlib-1.4.2 → foamlib-1.4.4}/src/foamlib/_cases/__init__.py +0 -0
  13. {foamlib-1.4.2 → foamlib-1.4.4}/src/foamlib/_cases/_run.py +0 -0
  14. {foamlib-1.4.2 → foamlib-1.4.4}/src/foamlib/_cases/_subprocess.py +0 -0
  15. {foamlib-1.4.2 → foamlib-1.4.4}/src/foamlib/_cases/_util.py +0 -0
  16. {foamlib-1.4.2 → foamlib-1.4.4}/src/foamlib/_cases/async_.py +0 -0
  17. {foamlib-1.4.2 → foamlib-1.4.4}/src/foamlib/_cases/slurm.py +0 -0
  18. {foamlib-1.4.2 → foamlib-1.4.4}/src/foamlib/_cases/sync.py +0 -0
  19. {foamlib-1.4.2 → foamlib-1.4.4}/src/foamlib/_files/__init__.py +0 -0
  20. {foamlib-1.4.2 → foamlib-1.4.4}/src/foamlib/_files/_common.py +0 -0
  21. {foamlib-1.4.2 → foamlib-1.4.4}/src/foamlib/_files/_io.py +0 -0
  22. {foamlib-1.4.2 → foamlib-1.4.4}/src/foamlib/_files/_parsing/__init__.py +0 -0
  23. {foamlib-1.4.2 → foamlib-1.4.4}/src/foamlib/_files/_parsing/_grammar.py +0 -0
  24. {foamlib-1.4.2 → foamlib-1.4.4}/src/foamlib/_files/_util.py +0 -0
  25. {foamlib-1.4.2 → foamlib-1.4.4}/src/foamlib/_files/types.py +0 -0
  26. {foamlib-1.4.2 → foamlib-1.4.4}/src/foamlib/postprocessing/__init__.py +0 -0
  27. {foamlib-1.4.2 → foamlib-1.4.4}/src/foamlib/postprocessing/load_tables.py +0 -0
  28. {foamlib-1.4.2 → foamlib-1.4.4}/src/foamlib/postprocessing/table_reader.py +0 -0
  29. {foamlib-1.4.2 → foamlib-1.4.4}/src/foamlib/preprocessing/__init__.py +0 -0
  30. {foamlib-1.4.2 → foamlib-1.4.4}/src/foamlib/preprocessing/case_modifier.py +0 -0
  31. {foamlib-1.4.2 → foamlib-1.4.4}/src/foamlib/preprocessing/grid_parameter_sweep.py +0 -0
  32. {foamlib-1.4.2 → foamlib-1.4.4}/src/foamlib/preprocessing/of_dict.py +0 -0
  33. {foamlib-1.4.2 → foamlib-1.4.4}/src/foamlib/preprocessing/parameter_study.py +0 -0
  34. {foamlib-1.4.2 → foamlib-1.4.4}/src/foamlib/preprocessing/system.py +0 -0
  35. {foamlib-1.4.2 → foamlib-1.4.4}/src/foamlib/py.typed +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: foamlib
3
- Version: 1.4.2
3
+ Version: 1.4.4
4
4
  Summary: Pythonic interface for working with OpenFOAM
5
5
  Author: Gabriel S. Gerlero
6
6
  Author-email: Gabriel S. Gerlero <ggerlero@cimec.unl.edu.ar>
@@ -330,4 +330,4 @@ If you use **foamlib** in your research, please cite our paper:
330
330
 
331
331
  ## 👟 Footnotes
332
332
 
333
- <a id="benchmark">[1]</a> foamlib 1.3.11 vs. PyFoam 2023.7 (Python 3.11.13) on an M3 MacBook Air. [Benchmark script](https://github.com/gerlero/foamlib/blob/main/benchmark/benchmark.py).
333
+ <a id="benchmark">[1]</a> foamlib 1.4.4 vs. PyFoam 2023.7 (Python 3.11.13) on an M3 MacBook Air. [Benchmark script](https://github.com/gerlero/foamlib/blob/main/benchmark/benchmark.py).
@@ -285,4 +285,4 @@ If you use **foamlib** in your research, please cite our paper:
285
285
 
286
286
  ## 👟 Footnotes
287
287
 
288
- <a id="benchmark">[1]</a> foamlib 1.3.11 vs. PyFoam 2023.7 (Python 3.11.13) on an M3 MacBook Air. [Benchmark script](https://github.com/gerlero/foamlib/blob/main/benchmark/benchmark.py).
288
+ <a id="benchmark">[1]</a> foamlib 1.4.4 vs. PyFoam 2023.7 (Python 3.11.13) on an M3 MacBook Air. [Benchmark script](https://github.com/gerlero/foamlib/blob/main/benchmark/benchmark.py).
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "foamlib"
7
- version = "1.4.2"
7
+ version = "1.4.4"
8
8
  license = "GPL-3.0-only"
9
9
  license-files = ["LICENSE.txt"]
10
10
  description = "Pythonic interface for working with OpenFOAM"
@@ -89,7 +89,7 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"], os.PathLike[str]):
89
89
  @override
90
90
  def __len__(self) -> int:
91
91
  """Return the number of field files in this time directory."""
92
- return len(list(iter(self)))
92
+ return sum(1 for _ in iter(self))
93
93
 
94
94
  def __delitem__(self, name: str, /) -> None:
95
95
  """Delete the field file with the given name in this time directory."""
@@ -31,11 +31,11 @@ from numpy.typing import DTypeLike
31
31
  from .._common import dict_from_items
32
32
  from .._typing import Dict, SubDict
33
33
 
34
+ _FLOAT_PATTERN = r"(?i:[+-]?(?:(?:\d+\.?\d*(?:e[+-]?\d+)?)|nan|inf(?:inity)?))"
35
+ _INT_PATTERN = r"-?\d+"
34
36
 
35
- class ASCIINumericList(ParserElement):
36
- _FLOAT_PATTERN = r"(?i:[+-]?(?:(?:\d+\.?\d*(?:e[+-]?\d+)?)|nan|inf(?:inity)?))"
37
- _INT_PATTERN = r"-?\d+"
38
37
 
38
+ class ASCIINumericList(ParserElement):
39
39
  def __init__(
40
40
  self,
41
41
  dtype: DTypeLike,
@@ -54,38 +54,41 @@ class ASCIINumericList(ParserElement):
54
54
  def _generateDefaultName(self) -> str:
55
55
  return self.name
56
56
 
57
- @override
58
- def parseImpl(
59
- self, instring: str, loc: int, do_actions: bool = True
57
+ def __parse(
58
+ self, instring: str, loc: int, *, assume_no_ignores: bool = False
60
59
  ) -> tuple[int, ParseResults]:
61
60
  spacing_pattern = "|".join(re.escape(c) for c in self.whiteChars)
62
61
  assert spacing_pattern
63
62
 
64
- assert all(
65
- isinstance(ignore_expr, Suppress) and isinstance(ignore_expr.expr, Regex)
66
- for ignore_expr in self.ignoreExprs
67
- )
68
- ignore_pattern = "|".join(
69
- ignore_expr.expr.re.pattern # ty: ignore[unresolved-attribute]
70
- for ignore_expr in self.ignoreExprs
71
- )
72
-
73
- if ignore_pattern:
74
- spacing_pattern = f"{spacing_pattern}|{ignore_pattern}"
63
+ if assume_no_ignores:
64
+ ignore_pattern = ""
65
+ else:
66
+ assert all(
67
+ isinstance(ignore_expr, Suppress)
68
+ and isinstance(ignore_expr.expr, Regex)
69
+ for ignore_expr in self.ignoreExprs
70
+ )
71
+ ignore_pattern = "|".join(
72
+ ignore_expr.expr.re.pattern # ty: ignore[unresolved-attribute]
73
+ for ignore_expr in self.ignoreExprs
74
+ )
75
+
76
+ if ignore_pattern:
77
+ spacing_pattern = f"{spacing_pattern}|{ignore_pattern}"
75
78
 
76
79
  if np.issubdtype(self._dtype, np.floating):
77
- base_pattern = self._FLOAT_PATTERN
80
+ number_pattern = _FLOAT_PATTERN
78
81
  elif np.issubdtype(self._dtype, np.integer):
79
- base_pattern = self._INT_PATTERN
82
+ number_pattern = _INT_PATTERN
80
83
  else:
81
84
  msg = f"Unsupported dtype {self._dtype}"
82
85
  raise TypeError(msg)
83
86
 
84
87
  if self._elshape:
85
88
  (dim,) = self._elshape
86
- element_pattern = rf"\((?:{spacing_pattern})*(?:{base_pattern}(?:{spacing_pattern})*){{{dim}}}\)"
89
+ element_pattern = rf"\((?:{spacing_pattern})*(?:{number_pattern}(?:{spacing_pattern})*){{{dim}}}\)"
87
90
  else:
88
- element_pattern = base_pattern
91
+ element_pattern = number_pattern
89
92
 
90
93
  regular_pattern = re.compile(
91
94
  rf"(\d*)(?:{spacing_pattern})*\((?:{spacing_pattern})*((?:{element_pattern}(?:{spacing_pattern})*)*)\)"
@@ -158,6 +161,15 @@ class ASCIINumericList(ParserElement):
158
161
  self,
159
162
  )
160
163
 
164
+ @override
165
+ def parseImpl(
166
+ self, instring: str, loc: int, do_actions: bool = True
167
+ ) -> tuple[int, ParseResults]:
168
+ try:
169
+ return self.__parse(instring, loc, assume_no_ignores=True)
170
+ except ParseException:
171
+ return self.__parse(instring, loc)
172
+
161
173
 
162
174
  _EAGER_INTEGER = Word(nums).add_parse_action(
163
175
  lambda tks: int(tks[0]), call_during_try=True
@@ -210,8 +222,6 @@ def binary_numeric_list(
210
222
 
211
223
 
212
224
  class ASCIIFacesLikeList(ParserElement):
213
- _INT_PATTERN = ASCIINumericList._INT_PATTERN
214
-
215
225
  def __init__(self) -> None:
216
226
  super().__init__()
217
227
  self.name = "ASCIIFacesLikeList"
@@ -220,27 +230,29 @@ class ASCIIFacesLikeList(ParserElement):
220
230
  def _generateDefaultName(self) -> str:
221
231
  return self.name
222
232
 
223
- @override
224
- def parseImpl(
225
- self, instring: str, loc: int, do_actions: bool = True
233
+ def __parse(
234
+ self, instring: str, loc: int, *, assume_no_ignores: bool = False
226
235
  ) -> tuple[int, ParseResults]:
227
236
  spacing_pattern = "|".join(re.escape(c) for c in self.whiteChars)
228
237
  assert spacing_pattern
229
238
 
230
- assert all(
231
- isinstance(ignore_expr, Suppress) and isinstance(ignore_expr.expr, Regex)
232
- for ignore_expr in self.ignoreExprs
233
- )
234
- ignore_pattern = "|".join(
235
- ignore_expr.expr.re.pattern # ty: ignore[unresolved-attribute]
236
- for ignore_expr in self.ignoreExprs
237
- )
238
-
239
- if ignore_pattern:
240
- spacing_pattern = f"{spacing_pattern}|{ignore_pattern}"
241
-
242
- three_face_pattern = rf"3(?:{spacing_pattern})*\((?:{spacing_pattern})*(?:{self._INT_PATTERN}(?:{spacing_pattern})*){{3}}\)"
243
- four_face_pattern = rf"4(?:{spacing_pattern})*\((?:{spacing_pattern})*(?:{self._INT_PATTERN}(?:{spacing_pattern})*){{4}}\)"
239
+ if assume_no_ignores:
240
+ ignore_pattern = ""
241
+ else:
242
+ assert all(
243
+ isinstance(ignore_expr, Suppress)
244
+ and isinstance(ignore_expr.expr, Regex)
245
+ for ignore_expr in self.ignoreExprs
246
+ )
247
+ ignore_pattern = "|".join(
248
+ ignore_expr.expr.re.pattern # ty: ignore[unresolved-attribute]
249
+ for ignore_expr in self.ignoreExprs
250
+ )
251
+ if ignore_pattern:
252
+ spacing_pattern = f"{spacing_pattern}|{ignore_pattern}"
253
+
254
+ three_face_pattern = rf"3(?:{spacing_pattern})*\((?:{spacing_pattern})*(?:{_INT_PATTERN}(?:{spacing_pattern})*){{3}}\)"
255
+ four_face_pattern = rf"4(?:{spacing_pattern})*\((?:{spacing_pattern})*(?:{_INT_PATTERN}(?:{spacing_pattern})*){{4}}\)"
244
256
 
245
257
  face_pattern = rf"(?:{three_face_pattern})|(?:{four_face_pattern})"
246
258
 
@@ -291,6 +303,15 @@ class ASCIIFacesLikeList(ParserElement):
291
303
  self,
292
304
  )
293
305
 
306
+ @override
307
+ def parseImpl(
308
+ self, instring: str, loc: int, do_actions: bool = True
309
+ ) -> tuple[int, ParseResults]:
310
+ try:
311
+ return self.__parse(instring, loc, assume_no_ignores=True)
312
+ except ParseException:
313
+ return self.__parse(instring, loc)
314
+
294
315
 
295
316
  def list_of(entry: ParserElement) -> ParserElement:
296
317
  return (
@@ -1,7 +1,12 @@
1
1
  import dataclasses
2
2
  import sys
3
3
  from collections.abc import Collection, Iterator, Sequence
4
- from typing import cast
4
+ from typing import cast, overload
5
+
6
+ if sys.version_info >= (3, 11):
7
+ from typing import Never, Unpack
8
+ else:
9
+ from typing_extensions import Never, Unpack
5
10
 
6
11
  if sys.version_info >= (3, 12):
7
12
  from typing import override
@@ -116,20 +121,63 @@ class ParsedFile(
116
121
  )
117
122
  return ret
118
123
 
124
+ @overload
125
+ @with_default
126
+ def getall(self, keywords: tuple[()], /) -> Collection[StandaloneData]: ...
127
+
128
+ @overload
129
+ @with_default
130
+ def getall(
131
+ self, keywords: tuple[str, Unpack[tuple[str, ...]]], /
132
+ ) -> Collection[Data | EllipsisType | None]: ...
133
+
119
134
  @override
120
135
  @with_default
121
136
  def getall(
122
137
  self, keywords: tuple[str, ...], /
123
- ) -> Collection[Data | StandaloneData | EllipsisType]:
138
+ ) -> Collection[Data | StandaloneData | EllipsisType | None]:
124
139
  return [entry.data for entry in self._parsed.getall(keywords)]
125
140
 
141
+ @overload
142
+ def __getitem__(self, keywords: tuple[()]) -> StandaloneData: ...
143
+
144
+ @overload
145
+ def __getitem__(
146
+ self, keywords: tuple[str, Unpack[tuple[str, ...]]]
147
+ ) -> Data | EllipsisType | None: ...
148
+
126
149
  @override
127
- def __setitem__(
128
- self, key: tuple[str, ...], value: Data | StandaloneData | EllipsisType | None
150
+ def __getitem__(
151
+ self, keywords: tuple[str, ...]
152
+ ) -> Data | StandaloneData | EllipsisType | None: # ty: ignore[invalid-method-override]
153
+ entry = self._parsed[keywords]
154
+ return entry.data
155
+
156
+ @override
157
+ def __setitem__( # ty: ignore[invalid-method-override]
158
+ self, key: Never, value: Never
129
159
  ) -> None: # pragma: no cover
130
160
  msg = "Use 'put' method instead"
131
161
  raise NotImplementedError(msg)
132
162
 
163
+ @overload
164
+ def put(
165
+ self,
166
+ keywords: tuple[()],
167
+ /,
168
+ data: StandaloneData,
169
+ content: bytes,
170
+ ) -> None: ...
171
+
172
+ @overload
173
+ def put(
174
+ self,
175
+ keywords: tuple[str, Unpack[tuple[str, ...]]],
176
+ /,
177
+ data: Data | EllipsisType | None,
178
+ content: bytes,
179
+ ) -> None: ...
180
+
133
181
  def put(
134
182
  self,
135
183
  keywords: tuple[str, ...],
@@ -143,6 +191,24 @@ class ParsedFile(
143
191
  self._parsed[keywords] = ParsedFile._Entry(data, start, start + len(content))
144
192
  self._remove_child_entries(keywords)
145
193
 
194
+ @overload
195
+ def add(
196
+ self,
197
+ keywords: tuple[()],
198
+ data: StandaloneData,
199
+ content: bytes,
200
+ /,
201
+ ) -> None: ...
202
+
203
+ @overload
204
+ def add(
205
+ self,
206
+ keywords: tuple[str, Unpack[tuple[str, ...]]],
207
+ data: Data | EllipsisType | None,
208
+ content: bytes,
209
+ /,
210
+ ) -> None: ...
211
+
146
212
  @override
147
213
  def add( # ty: ignore[invalid-method-override]
148
214
  self,
@@ -162,6 +228,16 @@ class ParsedFile(
162
228
  self._parsed.add(keywords, ParsedFile._Entry(data, start, end))
163
229
  self._update_content(start, end, content)
164
230
 
231
+ @overload
232
+ @with_default
233
+ def popone(self, keywords: tuple[()]) -> StandaloneData: ...
234
+
235
+ @overload
236
+ @with_default
237
+ def popone(
238
+ self, keywords: tuple[str, Unpack[tuple[str, ...]]]
239
+ ) -> Data | EllipsisType | None: ...
240
+
165
241
  @override
166
242
  @with_default
167
243
  def popone(
@@ -5,9 +5,9 @@ from typing import Literal, overload
5
5
  from warnings import warn
6
6
 
7
7
  if sys.version_info >= (3, 11):
8
- from typing import Never, assert_never, assert_type
8
+ from typing import Never, Unpack, assert_never, assert_type
9
9
  else:
10
- from typing_extensions import Never, assert_never, assert_type
10
+ from typing_extensions import Never, Unpack, assert_never, assert_type
11
11
 
12
12
  import numpy as np
13
13
 
@@ -44,7 +44,7 @@ def normalized(
44
44
  data: DataLike,
45
45
  /,
46
46
  *,
47
- keywords: tuple[str, ...] | None = ...,
47
+ keywords: tuple[str, Unpack[tuple[str, ...]]] | None = ...,
48
48
  format_: Literal["ascii", "binary"] | None = ...,
49
49
  ) -> Data: ...
50
50
 
@@ -64,21 +64,11 @@ def normalized(
64
64
  data: SubDictLike,
65
65
  /,
66
66
  *,
67
- keywords: tuple[str, ...] = ...,
67
+ keywords: tuple[str, Unpack[tuple[str, ...]]] = ...,
68
68
  format_: Literal["ascii", "binary"] | None = ...,
69
69
  ) -> SubDict: ...
70
70
 
71
71
 
72
- @overload
73
- def normalized(
74
- data: DataLike,
75
- /,
76
- *,
77
- keywords: tuple[str, ...] = ...,
78
- format_: Literal["ascii", "binary"] | None = ...,
79
- ) -> Data: ...
80
-
81
-
82
72
  @overload
83
73
  def normalized(
84
74
  data: DictLike,
@@ -94,7 +84,7 @@ def normalized(
94
84
  data: None,
95
85
  /,
96
86
  *,
97
- keywords: tuple[str, ...] = ...,
87
+ keywords: tuple[str, Unpack[tuple[str, ...]]] = ...,
98
88
  format_: Literal["ascii", "binary"] | None = ...,
99
89
  ) -> None: ...
100
90
 
@@ -1,6 +1,12 @@
1
+ import sys
1
2
  from collections.abc import Mapping, Sequence
2
3
  from typing import Literal, TypeAlias
3
4
 
5
+ if sys.version_info >= (3, 11):
6
+ from typing import Unpack
7
+ else:
8
+ from typing_extensions import Unpack
9
+
4
10
  import numpy as np
5
11
  from multicollections import MultiDict
6
12
 
@@ -38,8 +44,11 @@ DataEntryLike: TypeAlias = (
38
44
  DataEntry | Sequence["DataEntryLike | KeywordEntryLike | DictLike"] | FieldLike
39
45
  )
40
46
 
41
- Data: TypeAlias = DataEntry | tuple[DataEntry, ...]
42
- DataLike: TypeAlias = DataEntryLike | tuple[DataEntryLike, ...]
47
+ Data: TypeAlias = DataEntry | tuple[DataEntry, DataEntry, Unpack[tuple[DataEntry, ...]]]
48
+ DataLike: TypeAlias = (
49
+ DataEntryLike
50
+ | tuple[DataEntryLike, DataEntryLike, Unpack[tuple[DataEntryLike, ...]]]
51
+ )
43
52
 
44
53
  StandaloneDataEntry: TypeAlias = (
45
54
  DataEntry
@@ -57,9 +66,21 @@ StandaloneDataEntryLike: TypeAlias = (
57
66
  | Sequence[Sequence[int]]
58
67
  )
59
68
 
60
- StandaloneData: TypeAlias = StandaloneDataEntry | tuple[StandaloneDataEntry, ...]
69
+ StandaloneData: TypeAlias = (
70
+ StandaloneDataEntry
71
+ | tuple[
72
+ StandaloneDataEntry,
73
+ StandaloneDataEntry,
74
+ Unpack[tuple[StandaloneDataEntry, ...]],
75
+ ]
76
+ )
61
77
  StandaloneDataLike: TypeAlias = (
62
- StandaloneDataEntryLike | tuple[StandaloneDataEntryLike, ...]
78
+ StandaloneDataEntryLike
79
+ | tuple[
80
+ StandaloneDataEntryLike,
81
+ StandaloneDataEntryLike,
82
+ Unpack[tuple[StandaloneDataEntryLike, ...]],
83
+ ]
63
84
  )
64
85
 
65
86
  SubDict: TypeAlias = (
@@ -3,12 +3,12 @@ import os
3
3
  import sys
4
4
  from collections.abc import Collection, Iterable, Iterator, Mapping, Sequence
5
5
  from copy import deepcopy
6
- from typing import Literal, cast, overload
6
+ from typing import Literal, TypeVar, cast, overload
7
7
 
8
8
  if sys.version_info >= (3, 11):
9
- from typing import assert_never
9
+ from typing import Unpack, assert_never
10
10
  else:
11
- from typing_extensions import assert_never
11
+ from typing_extensions import Unpack, assert_never
12
12
 
13
13
  if sys.version_info >= (3, 12):
14
14
  from typing import override
@@ -21,7 +21,6 @@ from multicollections import MultiDict
21
21
  from multicollections.abc import MutableMultiMapping, with_default
22
22
 
23
23
  from .._files import _common
24
- from ._common import dict_from_items
25
24
  from ._io import FoamFileIO
26
25
  from ._parsing import parse
27
26
  from ._serialization import dumps, normalized
@@ -40,10 +39,12 @@ from ._typing import (
40
39
  from ._util import SupportsKeysAndGetItem
41
40
  from .types import Dimensioned, DimensionSet
42
41
 
42
+ _D = TypeVar("_D")
43
+
43
44
 
44
45
  class FoamFile(
45
46
  MutableMultiMapping[
46
- str | tuple[str, ...] | None,
47
+ str | None,
47
48
  "Data | StandaloneData | FoamFile.SubDict | None",
48
49
  ],
49
50
  os.PathLike[str],
@@ -110,7 +111,8 @@ class FoamFile(
110
111
 
111
112
  @override
112
113
  def __len__(self) -> int:
113
- return len(list(iter(self)))
114
+ with self._file:
115
+ return sum(1 for _ in iter(self))
114
116
 
115
117
  @override
116
118
  def __contains__(self, x: object) -> bool: # ty: ignore[invalid-method-override]
@@ -131,11 +133,17 @@ class FoamFile(
131
133
  ) -> Iterator["Data | StandaloneData | FoamFile.SubDict | None"]:
132
134
  for k, v in self._file._get_parsed().items():
133
135
  if k != ("FoamFile",) or self._include_header:
134
- yield v if v is not ... else FoamFile.SubDict(self._file, k)
136
+ if v is ...:
137
+ assert k
138
+ k = cast("tuple[str, Unpack[tuple[str, ...]]]", k)
139
+ yield FoamFile.SubDict(self._file, k)
140
+ else:
141
+ yield v
135
142
 
136
143
  @override
137
144
  def __len__(self) -> int:
138
- return len(list(iter(self)))
145
+ with self._file:
146
+ return sum(1 for _ in iter(self))
139
147
 
140
148
  @override
141
149
  def __contains__(self, value: object) -> bool:
@@ -151,29 +159,31 @@ class FoamFile(
151
159
  file: "FoamFile",
152
160
  *,
153
161
  include_header: bool = False,
154
- keywords: tuple[str, ...] = (),
155
162
  ) -> None:
156
- assert keywords or include_header
157
163
  self._file = file
158
164
  self._include_header = include_header
159
- self._keywords = keywords
160
165
 
161
166
  @override
162
167
  def __iter__(
163
168
  self,
164
169
  ) -> Iterator[
165
- tuple[str | None, "Data | StandaloneData | FoamFile.SubDict | None"]
170
+ tuple[str, "Data | FoamFile.SubDict | None"] | tuple[None, StandaloneData]
166
171
  ]:
167
172
  for k, v in self._file._get_parsed().items():
168
173
  if k != ("FoamFile",) or self._include_header:
169
- yield (
170
- k[-1] if k else None,
171
- v if v is not ... else FoamFile.SubDict(self._file, k),
172
- )
174
+ if not k:
175
+ yield None, v
176
+ else:
177
+ k = cast("tuple[str, Unpack[tuple[str, ...]]]", k)
178
+ yield (
179
+ k[-1],
180
+ v if v is not ... else FoamFile.SubDict(self._file, k),
181
+ )
173
182
 
174
183
  @override
175
184
  def __len__(self) -> int:
176
- return len(list(iter(self)))
185
+ with self._file:
186
+ return sum(1 for _ in iter(self))
177
187
 
178
188
  @override
179
189
  def __contains__(self, x: object) -> bool: # ty: ignore[invalid-method-override]
@@ -221,7 +231,8 @@ class FoamFile(
221
231
 
222
232
  @override
223
233
  def __len__(self) -> int:
224
- return len(list(iter(self)))
234
+ with self._subdict._file:
235
+ return sum(1 for _ in iter(self))
225
236
 
226
237
  @override
227
238
  def __contains__(self, x: object) -> bool: # ty: ignore[invalid-method-override]
@@ -237,6 +248,7 @@ class FoamFile(
237
248
  def __iter__(self) -> Iterator["Data | FoamFile.SubDict | None"]:
238
249
  for k, v in self._subdict._file._get_parsed().items():
239
250
  if k[:-1] == self._subdict._keywords:
251
+ k = cast("tuple[str, Unpack[tuple[str, ...]]]", k)
240
252
  yield (
241
253
  v
242
254
  if v is not ...
@@ -245,7 +257,8 @@ class FoamFile(
245
257
 
246
258
  @override
247
259
  def __len__(self) -> int:
248
- return len(list(iter(self)))
260
+ with self._subdict._file:
261
+ return sum(1 for _ in iter(self))
249
262
 
250
263
  @override
251
264
  def __contains__(self, value: object) -> bool:
@@ -263,8 +276,9 @@ class FoamFile(
263
276
  ) -> Iterator[tuple[str, "Data | FoamFile.SubDict | None"]]:
264
277
  for k, v in self._subdict._file._get_parsed().items():
265
278
  if k[:-1] == self._subdict._keywords:
279
+ k = cast("tuple[str, Unpack[tuple[str, ...]]]", k)
266
280
  yield (
267
- k[-1] if k else None,
281
+ k[-1],
268
282
  v
269
283
  if v is not ...
270
284
  else FoamFile.SubDict(self._subdict._file, k),
@@ -272,45 +286,29 @@ class FoamFile(
272
286
 
273
287
  @override
274
288
  def __len__(self) -> int:
275
- return len(list(iter(self)))
289
+ with self._subdict._file:
290
+ return sum(1 for _ in iter(self))
276
291
 
277
292
  @override
278
293
  def __contains__(self, x: object) -> bool: # ty: ignore[invalid-method-override]
279
294
  return any(i == x for i in iter(self))
280
295
 
281
- def __init__(self, _file: "FoamFile", _keywords: tuple[str, ...]) -> None:
296
+ def __init__(
297
+ self, _file: "FoamFile", _keywords: tuple[str, Unpack[tuple[str, ...]]]
298
+ ) -> None:
282
299
  self._file = _file
283
300
  self._keywords = _keywords
284
301
 
285
302
  @override
286
303
  @with_default
287
- def getall(self, keyword: str) -> Collection["Data | FoamFile.SubDict | None"]:
304
+ def getall(
305
+ self, keyword: str, /
306
+ ) -> Collection["Data | FoamFile.SubDict | None"]:
288
307
  return self._file.getall((*self._keywords, keyword))
289
308
 
290
- @overload
291
- def __getitem__(self, keyword: str) -> "Data | FoamFile.SubDict | None": ...
292
-
293
- @overload
294
- def __getitem__(
295
- self, keyword: slice
296
- ) -> (
297
- dict[str, "Data | FoamFile.SubDict | None"]
298
- | MultiDict[str, "Data | FoamFile.SubDict | None"]
299
- ): ...
300
-
301
309
  @override
302
- def __getitem__(
303
- self, keyword: str | slice
304
- ) -> "Data | FoamFile.SubDict | None | dict[str, Data | FoamFile.SubDict | None] | MultiDict[str, Data | FoamFile.SubDict | None]":
305
- if keyword == slice(None):
306
- return self.as_dict(deep=False)
307
-
308
- if isinstance(keyword, slice):
309
- msg = "Only empty slices (:) are supported"
310
- raise ValueError(msg) # noqa: TRY004
311
-
312
- ret = self._file[(*self._keywords, keyword)]
313
- return cast("Data | FoamFile.SubDict | None", ret)
310
+ def __getitem__(self, keyword: str) -> "Data | FoamFile.SubDict | None":
311
+ return self._file[(*self._keywords, keyword)] # ty: ignore[invalid-return-type]
314
312
 
315
313
  @overload
316
314
  def __setitem__(
@@ -345,7 +343,7 @@ class FoamFile(
345
343
 
346
344
  @override
347
345
  @with_default
348
- def popone(self, keyword: str) -> "Data | FoamFile.SubDict | None":
346
+ def popone(self, keyword: str, /) -> "Data | FoamFile.SubDict | None":
349
347
  return self._file.popone((*self._keywords, keyword))
350
348
 
351
349
  @override
@@ -372,7 +370,8 @@ class FoamFile(
372
370
 
373
371
  @override
374
372
  def __len__(self) -> int:
375
- return len(list(iter(self)))
373
+ with self._file:
374
+ return sum(1 for _ in iter(self))
376
375
 
377
376
  @override
378
377
  def keys(self) -> "FoamFile.SubDict.KeysView":
@@ -428,42 +427,17 @@ class FoamFile(
428
427
  def __repr__(self) -> str:
429
428
  return f"{type(self).__qualname__}('{self._file}', {self._keywords})"
430
429
 
431
- @overload
432
- def as_dict(self, *, deep: Literal[True] = ...) -> SubDict: ...
433
-
434
- @overload
435
- def as_dict(
436
- self, *, deep: Literal[False] = ...
437
- ) -> (
438
- dict[str, "Data | FoamFile.SubDict | None"]
439
- | MultiDict[str, "Data | FoamFile.SubDict | None"]
440
- ): ...
441
-
442
- def as_dict(
443
- self, *, deep: bool = True
444
- ) -> (
445
- SubDict
446
- | dict[str, "Data | FoamFile.SubDict | None"]
447
- | MultiDict[str, "Data | FoamFile.SubDict | None"]
448
- ):
449
- """
450
- Return a dict representation of the sub-dictionary.
451
-
452
- :param deep: Whether to return a nested representation by recursing into
453
- nested sub-dictionaries.
454
- """
455
- if deep:
456
- file = self._file.as_dict(include_header=True)
430
+ def as_dict(self) -> SubDict:
431
+ """Return a nested dict representation of the sub-dictionary."""
432
+ file = self._file.as_dict(include_header=True)
457
433
 
458
- ret = file[self._keywords[0]]
434
+ ret = file[self._keywords[0]]
435
+ assert isinstance(ret, Mapping)
436
+ for k in self._keywords[1:]:
437
+ ret = ret[k]
459
438
  assert isinstance(ret, Mapping)
460
- for k in self._keywords[1:]:
461
- ret = ret[k]
462
- assert isinstance(ret, Mapping)
463
-
464
- return ret # ty: ignore[invalid-return-type]
465
439
 
466
- return dict_from_items(self.items(), target=SubDict)
440
+ return ret # ty: ignore[invalid-return-type]
467
441
 
468
442
  @property
469
443
  def version(self) -> float:
@@ -552,10 +526,21 @@ class FoamFile(
552
526
  self.path.stem if self.path.suffix == ".gz" else self.path.name
553
527
  )
554
528
 
529
+ @overload
555
530
  def _update_class_for_field_if_needed(
556
531
  self,
557
- keywords: tuple[str, ...],
558
- data: Data | StandaloneData | SubDict,
532
+ keywords: tuple[str, Unpack[tuple[str, ...]]],
533
+ data: Data | SubDict | None,
534
+ /,
535
+ ) -> None: ...
536
+
537
+ @overload
538
+ def _update_class_for_field_if_needed(
539
+ self, keywords: tuple[()], data: StandaloneData, /
540
+ ) -> None: ...
541
+
542
+ def _update_class_for_field_if_needed(
543
+ self, keywords: tuple[str, ...], data: Data | StandaloneData | SubDict, /
559
544
  ) -> None:
560
545
  """Update class field to appropriate field type if this is a field entry."""
561
546
  try:
@@ -620,6 +605,26 @@ class FoamFile(
620
605
 
621
606
  return before, after
622
607
 
608
+ @overload
609
+ def _perform_entry_operation(
610
+ self,
611
+ keywords: str | tuple[str, Unpack[tuple[str, ...]]],
612
+ data: DataLike | SubDictLike | None,
613
+ /,
614
+ *,
615
+ add: bool,
616
+ ) -> None: ...
617
+
618
+ @overload
619
+ def _perform_entry_operation(
620
+ self,
621
+ keywords: None | tuple[()],
622
+ data: StandaloneDataLike,
623
+ /,
624
+ *,
625
+ add: bool,
626
+ ) -> None: ...
627
+
623
628
  def _perform_entry_operation(
624
629
  self,
625
630
  keywords: tuple[str, ...],
@@ -663,6 +668,7 @@ class FoamFile(
663
668
  if not keywords:
664
669
  msg = "Cannot set a mapping at the root level of a FoamFile\nUse update(), extend(), or merge() instead."
665
670
  raise ValueError(msg)
671
+ keywords = cast("tuple[str, Unpack[tuple[str, ...]]]", keywords)
666
672
 
667
673
  if keyword.startswith("#"):
668
674
  msg = f"Cannot set a directive as the keyword for a dictionary: {keyword}"
@@ -688,6 +694,7 @@ class FoamFile(
688
694
  self[(*keywords, k)] = v # ty: ignore[invalid-assignment]
689
695
 
690
696
  elif keywords:
697
+ keywords = cast("tuple[str, Unpack[tuple[str, ...]]]", keywords)
691
698
  val = dumps(data, keywords=keywords, format_=format_)
692
699
 
693
700
  content = (
@@ -718,94 +725,121 @@ class FoamFile(
718
725
  @overload
719
726
  @with_default
720
727
  def getall(
721
- self, keywords: str | tuple[str]
728
+ self,
729
+ keywords: str | tuple[str, Unpack[tuple[str, ...]]],
730
+ /,
722
731
  ) -> Collection["Data | FoamFile.SubDict | None"]: ...
723
732
 
724
733
  @overload
725
734
  @with_default
726
- def getall(self, keywords: None | tuple[()]) -> Collection[StandaloneData]: ...
727
-
728
- @overload
729
- @with_default
730
- def getall(
731
- self, keywords: tuple[str, ...]
732
- ) -> Collection["Data | StandaloneData | FoamFile.SubDict | None"]: ...
735
+ def getall(self, keywords: None | tuple[()], /) -> Collection[StandaloneData]: ...
733
736
 
734
737
  @override
735
738
  @with_default
736
739
  def getall(
737
740
  self,
738
741
  keywords: str | tuple[str, ...] | None,
742
+ /,
739
743
  ) -> Collection["Data | StandaloneData | FoamFile.SubDict | None"]:
740
744
  keywords = FoamFile._normalized_keywords(keywords)
745
+ keywords = cast("tuple[str, Unpack[tuple[str, ...]]] | tuple[()]", keywords)
741
746
 
742
747
  parsed = self._get_parsed()
743
748
 
744
- ret = parsed.getall(keywords)
749
+ values = parsed.getall(keywords)
745
750
 
746
- return [
747
- FoamFile.SubDict(self, keywords) if v is ... else deepcopy(v) for v in ret
748
- ]
751
+ ret = []
752
+ for v in values:
753
+ if v is ...:
754
+ assert keywords
755
+ ret.append(FoamFile.SubDict(self, keywords))
756
+ else:
757
+ ret.append(deepcopy(v))
758
+ return ret
749
759
 
750
760
  @overload
751
- def __getitem__(
761
+ @with_default
762
+ def getone(
752
763
  self,
753
- keywords: str | tuple[str],
764
+ keywords: str | tuple[str, Unpack[tuple[str, ...]]],
765
+ /,
754
766
  ) -> "Data | FoamFile.SubDict | None": ...
755
767
 
756
768
  @overload
757
- def __getitem__(
769
+ @with_default
770
+ def getone(self, keywords: None | tuple[()], /) -> StandaloneData: ...
771
+
772
+ @override
773
+ @with_default
774
+ def getone(
775
+ self,
776
+ keywords: str | tuple[str, ...] | None,
777
+ /,
778
+ ) -> "Data | StandaloneData | FoamFile.SubDict | None":
779
+ keywords = FoamFile._normalized_keywords(keywords)
780
+
781
+ parsed = self._get_parsed()
782
+ keywords = cast("tuple[str, Unpack[tuple[str, ...]]] | tuple[()]", keywords)
783
+ ret = parsed[keywords]
784
+ if ret is ...:
785
+ assert keywords
786
+ return FoamFile.SubDict(self, keywords)
787
+ return deepcopy(ret)
788
+
789
+ @overload
790
+ def get(
791
+ self,
792
+ keywords: str | tuple[str, Unpack[tuple[str, ...]]],
793
+ default: _D = ...,
794
+ /,
795
+ ) -> "Data | FoamFile.SubDict | None | _D": ...
796
+
797
+ @overload
798
+ def get(
758
799
  self,
759
800
  keywords: None | tuple[()],
760
- ) -> StandaloneData: ...
801
+ default: _D = ...,
802
+ /,
803
+ ) -> StandaloneData | _D: ...
804
+
805
+ @override
806
+ def get( # ty: ignore[invalid-method-override]
807
+ self,
808
+ keywords: str | tuple[str, ...] | None,
809
+ default: _D = None, # ty: ignore[invalid-parameter-default]
810
+ /,
811
+ ) -> "Data | StandaloneData | FoamFile.SubDict | None | _D":
812
+ return self.getone(keywords, default=default)
761
813
 
762
814
  @overload
763
815
  def __getitem__(
764
816
  self,
765
- keywords: tuple[str, ...],
766
- ) -> "Data | StandaloneData | FoamFile.SubDict | None": ...
817
+ keywords: str | tuple[str, Unpack[tuple[str, ...]]],
818
+ ) -> "Data | FoamFile.SubDict | None": ...
767
819
 
768
820
  @overload
769
821
  def __getitem__(
770
822
  self,
771
- keywords: slice,
772
- ) -> (
773
- dict[str | None, "Data | StandaloneData | FoamFile.SubDict | None"]
774
- | MultiDict[str | None, "Data | StandaloneData | FoamFile.SubDict | None"]
775
- ): ...
823
+ keywords: None | tuple[()],
824
+ ) -> StandaloneData: ...
776
825
 
777
826
  @override
778
827
  def __getitem__( # ty: ignore[invalid-method-override]
779
828
  self,
780
- keywords: str | tuple[str, ...] | None | slice,
781
- ) -> "Data | StandaloneData | FoamFile.SubDict | None | dict[str | None, Data | StandaloneData | FoamFile.SubDict | None] | MultiDict[str | None, Data | StandaloneData | FoamFile.SubDict | None]":
782
- keywords = FoamFile._normalized_keywords(keywords, slice_ok=True)
783
-
784
- if keywords == slice(None):
785
- return self.as_dict(deep=False)
786
-
787
- assert not isinstance(keywords, slice)
788
- parsed = self._get_parsed()
789
- ret = parsed[keywords]
790
- if ret is ...:
791
- return FoamFile.SubDict(self, keywords)
792
- return deepcopy(ret)
793
-
794
- @overload
795
- def __setitem__(
796
- self, keywords: None | tuple[()], data: StandaloneDataLike
797
- ) -> None: ...
829
+ keywords: str | tuple[str, ...] | None,
830
+ ) -> "Data | StandaloneData | FoamFile.SubDict | None":
831
+ return self.getone(keywords)
798
832
 
799
833
  @overload
800
834
  def __setitem__(
801
- self, keywords: str, data: DataLike | SubDictLike | None
835
+ self,
836
+ keywords: str | tuple[str, Unpack[tuple[str, ...]]],
837
+ data: DataLike | SubDictLike | None,
802
838
  ) -> None: ...
803
839
 
804
840
  @overload
805
841
  def __setitem__(
806
- self,
807
- keywords: tuple[str, ...],
808
- data: DataLike | StandaloneDataLike | SubDictLike | None,
842
+ self, keywords: None | tuple[()], data: StandaloneDataLike
809
843
  ) -> None: ...
810
844
 
811
845
  @overload
@@ -834,7 +868,7 @@ class FoamFile(
834
868
  return
835
869
 
836
870
  assert not isinstance(keywords, slice)
837
- self._perform_entry_operation(keywords, data, add=False) # ty: ignore[invalid-argument-type]
871
+ self._perform_entry_operation(keywords, data, add=False) # ty: ignore[invalid-argument-type,no-matching-overload]
838
872
 
839
873
  @override
840
874
  def __delitem__(self, keywords: str | tuple[str, ...] | None | slice) -> None:
@@ -846,22 +880,52 @@ class FoamFile(
846
880
  return
847
881
 
848
882
  assert not isinstance(keywords, slice)
849
- super().__delitem__(keywords)
850
883
 
851
- @override
884
+ with self:
885
+ del self._get_parsed()[keywords]
886
+
887
+ @overload
888
+ def add(
889
+ self,
890
+ keywords: str | tuple[str, Unpack[tuple[str, ...]]],
891
+ data: DataLike | SubDictLike | None,
892
+ ) -> None: ...
893
+
894
+ @overload
852
895
  def add(
896
+ self,
897
+ keywords: None | tuple[()],
898
+ data: StandaloneDataLike,
899
+ ) -> None: ...
900
+
901
+ @override
902
+ def add( # ty: ignore[invalid-method-override]
853
903
  self,
854
904
  keywords: str | tuple[str, ...] | None,
855
905
  data: DataLike | StandaloneDataLike | SubDictLike | None,
856
906
  ) -> None:
857
907
  keywords = FoamFile._normalized_keywords(keywords)
858
-
908
+ keywords = cast("tuple[str, Unpack[tuple[str, ...]]] | tuple[()]", keywords)
859
909
  self._perform_entry_operation(keywords, data, add=True)
860
910
 
911
+ @overload
861
912
  @with_default
913
+ def popone(
914
+ self,
915
+ keywords: str | tuple[str, Unpack[tuple[str, ...]]],
916
+ /,
917
+ ) -> "Data | FoamFile.SubDict | None": ...
918
+
919
+ @overload
920
+ @with_default
921
+ def popone(self, keywords: None | tuple[()], /) -> StandaloneData: ...
922
+
862
923
  @override
924
+ @with_default
863
925
  def popone(
864
- self, keywords: str | tuple[str, ...] | None
926
+ self,
927
+ keywords: str | tuple[str, ...] | None,
928
+ /,
865
929
  ) -> "Data | StandaloneData | FoamFile.SubDict | None":
866
930
  keywords = FoamFile._normalized_keywords(keywords)
867
931
 
@@ -870,12 +934,15 @@ class FoamFile(
870
934
 
871
935
  @overload
872
936
  def _iter(
873
- self, keywords: tuple[str, ...], *, include_header: bool = False
937
+ self,
938
+ keywords: tuple[str, Unpack[tuple[str, ...]]],
939
+ *,
940
+ include_header: bool = ...,
874
941
  ) -> Iterator[str]: ...
875
942
 
876
943
  @overload
877
944
  def _iter(
878
- self, keywords: tuple[()] = (), *, include_header: bool = False
945
+ self, keywords: tuple[()] = ..., *, include_header: bool = ...
879
946
  ) -> Iterator[str | None]: ...
880
947
 
881
948
  def _iter(
@@ -905,7 +972,8 @@ class FoamFile(
905
972
  @override
906
973
  def __len__(self) -> int:
907
974
  """Return the number of top-level keywords in the FoamFile (excluding the FoamFile header if present)."""
908
- return len(list(iter(self)))
975
+ with self:
976
+ return sum(1 for _ in self._iter())
909
977
 
910
978
  @override
911
979
  def keys(
@@ -950,12 +1018,12 @@ class FoamFile(
950
1018
  def update( # ty: ignore[invalid-method-override]
951
1019
  self,
952
1020
  other: SupportsKeysAndGetItem[
953
- str | tuple[str, ...] | None,
1021
+ str | None,
954
1022
  DataLike | StandaloneDataLike | SubDictLike | None,
955
1023
  ]
956
1024
  | Iterable[
957
1025
  tuple[
958
- str | tuple[str, ...] | None,
1026
+ str | None,
959
1027
  DataLike | StandaloneDataLike | SubDictLike | None,
960
1028
  ]
961
1029
  ] = (),
@@ -969,12 +1037,12 @@ class FoamFile(
969
1037
  def extend( # ty: ignore[invalid-method-override]
970
1038
  self,
971
1039
  other: SupportsKeysAndGetItem[
972
- str | tuple[str, ...] | None,
1040
+ str | None,
973
1041
  DataLike | StandaloneDataLike | SubDictLike | None,
974
1042
  ]
975
1043
  | Iterable[
976
1044
  tuple[
977
- str | tuple[str, ...] | None,
1045
+ str | None,
978
1046
  DataLike | StandaloneDataLike | SubDictLike | None,
979
1047
  ]
980
1048
  ] = (),
@@ -988,14 +1056,10 @@ class FoamFile(
988
1056
  def merge( # ty: ignore[invalid-method-override]
989
1057
  self,
990
1058
  other: SupportsKeysAndGetItem[
991
- str | tuple[str, ...] | None,
992
- DataLike | StandaloneDataLike | SubDictLike | None,
1059
+ str | None, DataLike | StandaloneDataLike | SubDictLike | None
993
1060
  ]
994
1061
  | Iterable[
995
- tuple[
996
- str | tuple[str, ...] | None,
997
- DataLike | StandaloneDataLike | SubDictLike | None,
998
- ]
1062
+ tuple[str | None, DataLike | StandaloneDataLike | SubDictLike | None]
999
1063
  ] = (),
1000
1064
  /,
1001
1065
  **kwargs: DataLike | StandaloneDataLike | SubDictLike | None,
@@ -1020,40 +1084,16 @@ class FoamFile(
1020
1084
  def __fspath__(self) -> str:
1021
1085
  return str(self.path)
1022
1086
 
1023
- @overload
1024
- def as_dict(
1025
- self, *, include_header: bool = ..., deep: Literal[True] = ...
1026
- ) -> File: ...
1027
-
1028
- @overload
1029
- def as_dict(
1030
- self, *, include_header: bool = ..., deep: Literal[False] = ...
1031
- ) -> (
1032
- dict[str | None, "Data | StandaloneData | FoamFile.SubDict | None"]
1033
- | MultiDict[str | None, "Data | StandaloneData | FoamFile.SubDict | None"]
1034
- ): ...
1035
-
1036
- def as_dict(
1037
- self, *, include_header: bool = False, deep: bool = True
1038
- ) -> (
1039
- File
1040
- | dict[str | None, "Data | StandaloneData | FoamFile.SubDict | None"]
1041
- | MultiDict[str | None, "Data | StandaloneData | FoamFile.SubDict | None"]
1042
- ):
1087
+ def as_dict(self, *, include_header: bool = False) -> File:
1043
1088
  """
1044
- Return a dict representation of the file.
1089
+ Return a nested dict representation of the file.
1045
1090
 
1046
1091
  :param include_header: Whether to include the "FoamFile" header in the output.
1047
- :param deep: Whether to return a nested representation by recursing into
1048
- nested sub-dictionaries.
1049
1092
  """
1050
- if deep:
1051
- d = self._get_parsed().as_dict()
1052
- if not include_header:
1053
- d.pop("FoamFile", None)
1054
- return deepcopy(d)
1055
-
1056
- return dict_from_items(self.items(include_header=include_header), target=File) # ty: ignore[invalid-return-type]
1093
+ d = self._get_parsed().as_dict()
1094
+ if not include_header:
1095
+ d.pop("FoamFile", None)
1096
+ return deepcopy(d)
1057
1097
 
1058
1098
  @staticmethod
1059
1099
  def loads(
@@ -1135,14 +1175,14 @@ class FoamFile(
1135
1175
  @overload
1136
1176
  @staticmethod
1137
1177
  def _normalized_keywords(
1138
- keywords: None, /, *, slice_ok: bool = ...
1139
- ) -> tuple[()]: ...
1178
+ keywords: tuple[str, Unpack[tuple[str, ...]]], /, *, slice_ok: bool = ...
1179
+ ) -> tuple[str, Unpack[tuple[str, ...]]]: ...
1140
1180
 
1141
1181
  @overload
1142
1182
  @staticmethod
1143
1183
  def _normalized_keywords(
1144
- keywords: tuple[str, ...], /, *, slice_ok: bool = ...
1145
- ) -> tuple[str, ...]: ...
1184
+ keywords: None | tuple[()], /, *, slice_ok: bool = ...
1185
+ ) -> tuple[()]: ...
1146
1186
 
1147
1187
  @overload
1148
1188
  @staticmethod
@@ -1225,7 +1265,7 @@ class FoamFieldFile(FoamFile):
1225
1265
  @override
1226
1266
  @with_default
1227
1267
  def getall(
1228
- self, keyword: str
1268
+ self, keyword: str, /
1229
1269
  ) -> Collection["FoamFieldFile.BoundarySubDict | Data | None"]:
1230
1270
  ret = super().getall(keyword)
1231
1271
  for r in ret:
@@ -1273,23 +1313,17 @@ class FoamFieldFile(FoamFile):
1273
1313
  @overload
1274
1314
  @with_default
1275
1315
  def getall(
1276
- self, keywords: str | tuple[str]
1277
- ) -> Collection["Data | FoamFieldFile.BoundariesSubDict | None"]: ...
1316
+ self, keywords: str | tuple[str, Unpack[tuple[str, ...]]], /
1317
+ ) -> Collection["Data | FoamFieldFile.SubDict | None"]: ...
1278
1318
 
1279
1319
  @overload
1280
1320
  @with_default
1281
- def getall(self, keywords: None | tuple[()]) -> Collection[StandaloneData]: ...
1282
-
1283
- @overload
1284
- @with_default
1285
- def getall(
1286
- self, keywords: tuple[str, ...]
1287
- ) -> Collection["Data | StandaloneData | FoamFieldFile.SubDict | None"]: ...
1321
+ def getall(self, keywords: None | tuple[()], /) -> Collection[StandaloneData]: ...
1288
1322
 
1289
1323
  @override
1290
1324
  @with_default
1291
1325
  def getall(
1292
- self, keywords: str | tuple[str, ...] | None
1326
+ self, keywords: str | tuple[str, ...] | None, /
1293
1327
  ) -> Collection["Data | StandaloneData | FoamFieldFile.SubDict | None"]:
1294
1328
  keywords = FoamFieldFile._normalized_keywords(keywords)
1295
1329
 
@@ -1299,8 +1333,10 @@ class FoamFieldFile(FoamFile):
1299
1333
  for i, r in enumerate(ret):
1300
1334
  if isinstance(r, FoamFile.SubDict):
1301
1335
  if len(keywords) == 1:
1336
+ keywords = cast("tuple[str]", keywords)
1302
1337
  ret[i] = FoamFieldFile.BoundariesSubDict(self, keywords)
1303
1338
  elif len(keywords) == 2:
1339
+ keywords = cast("tuple[str, str]", keywords)
1304
1340
  ret[i] = FoamFieldFile.BoundarySubDict(self, keywords)
1305
1341
 
1306
1342
  return ret
@@ -1308,14 +1344,8 @@ class FoamFieldFile(FoamFile):
1308
1344
  @overload
1309
1345
  def __getitem__(
1310
1346
  self,
1311
- keywords: str | tuple[str],
1312
- ) -> "Data | FoamFieldFile.BoundariesSubDict | None": ...
1313
-
1314
- @overload
1315
- def __getitem__(
1316
- self,
1317
- keywords: tuple[str, str],
1318
- ) -> "Data | FoamFieldFile.BoundarySubDict | None": ...
1347
+ keywords: str | tuple[str, Unpack[tuple[str, ...]]],
1348
+ ) -> "Data | FoamFieldFile.SubDict | None": ...
1319
1349
 
1320
1350
  @overload
1321
1351
  def __getitem__(
@@ -1323,35 +1353,25 @@ class FoamFieldFile(FoamFile):
1323
1353
  keywords: None | tuple[()],
1324
1354
  ) -> StandaloneData: ...
1325
1355
 
1326
- @overload
1327
- def __getitem__(
1328
- self,
1329
- keywords: tuple[str, ...],
1330
- ) -> "Data | StandaloneData | FoamFieldFile.SubDict | None": ...
1331
-
1332
- @overload
1333
- def __getitem__(
1334
- self,
1335
- keywords: slice,
1336
- ) -> File: ...
1337
-
1338
1356
  @override
1339
1357
  def __getitem__( # ty: ignore[invalid-method-override]
1340
1358
  self,
1341
- keywords: str | tuple[str, ...] | None | slice,
1342
- ) -> "Data | StandaloneData | FoamFieldFile.SubDict | None | dict[str | None, Data | StandaloneData | FoamFieldFile.SubDict | None] | MultiDict[str | None, Data | StandaloneData | FoamFieldFile.SubDict | None]":
1343
- keywords = FoamFieldFile._normalized_keywords(keywords, slice_ok=True)
1344
-
1345
- if keywords == slice(None):
1346
- return self.as_dict(deep=False)
1359
+ keywords: str | tuple[str, ...] | None,
1360
+ ) -> "Data | StandaloneData | FoamFieldFile.SubDict | None":
1361
+ keywords = FoamFieldFile._normalized_keywords(keywords)
1347
1362
 
1348
- assert not isinstance(keywords, slice)
1349
- ret = super().__getitem__(keywords)
1363
+ ret = super().__getitem__(keywords) # ty: ignore[no-matching-overload]
1350
1364
 
1351
- if keywords[0] == "boundaryField" and isinstance(ret, FoamFile.SubDict):
1365
+ if (
1366
+ keywords
1367
+ and keywords[0] == "boundaryField"
1368
+ and isinstance(ret, FoamFile.SubDict)
1369
+ ):
1352
1370
  if len(keywords) == 1:
1371
+ keywords = cast("tuple[str]", keywords)
1353
1372
  ret = FoamFieldFile.BoundariesSubDict(self, keywords)
1354
1373
  elif len(keywords) == 2:
1374
+ keywords = cast("tuple[str, str]", keywords)
1355
1375
  ret = FoamFieldFile.BoundarySubDict(self, keywords)
1356
1376
 
1357
1377
  return ret
File without changes
File without changes
File without changes